From c1c7ba6d04e1d2a557dbbf46b5032dab6a4ead16 Mon Sep 17 00:00:00 2001 From: Ben Tatum Date: Sat, 23 May 2026 14:14:19 -0400 Subject: [PATCH] Release v2 SDK --- .changeset/README.md | 14 - .changeset/config.json | 12 - .../fix-eip3009-clock-skew-and-fee-payer.md | 10 - .eslintrc.json | 22 - .github/PULL_REQUEST_TEMPLATE.md | 29 - .github/workflows/ci.yml | 33 +- .github/workflows/release.yml | 57 +- .gitignore | 49 +- .husky/pre-commit | 34 - .husky/pre-push | 35 - .nvmrc | 2 - .prettierignore | 11 - .prettierrc.json | 9 - CHANGELOG.md | 8 + CONTRIBUTING.md | 114 - QUICK-START.md | 97 - README.md | 130 +- examples/hello-world/.env.example | 5 - examples/hello-world/.gitignore | 6 - examples/hello-world/README.md | 181 - examples/hello-world/command.ts | 54 - examples/hello-world/lib/balance.ts | 30 - examples/hello-world/lib/config.ts | 29 - examples/hello-world/lib/payment.ts | 68 - examples/hello-world/lib/wallet.ts | 52 - examples/hello-world/package.json | 25 - examples/hello-world/prompt.ts | 88 - examples/hello-world/tsconfig.json | 16 - .../README.md | 142 - .../lib/balance.ts | 30 - .../lib/config.ts | 29 - .../lib/payment.ts | 68 - .../lib/wallet.ts | 52 - .../package.json | 27 - .../test-1-single-arg.ts | 70 - .../test-2-single-arg-quoted.ts | 70 - .../test-3-cli-flags.ts | 75 - .../test-4-json-format.ts | 81 - .../test-5-mixed-format.ts | 76 - .../tsconfig.json | 16 - examples/integration-test/README.md | 137 - examples/integration-test/lib/balance.ts | 30 - examples/integration-test/lib/config.ts | 29 - examples/integration-test/lib/payment.ts | 68 - examples/integration-test/lib/wallet.ts | 52 - examples/integration-test/package.json | 34 - examples/integration-test/test-all.ts | 117 - examples/integration-test/test.ts | 264 - examples/integration-test/tsconfig.json | 16 - examples/marketputer/.gitignore | 6 - examples/marketputer/README.md | 639 -- examples/marketputer/brands/README.md | 45 - examples/marketputer/brands/memeputer.json | 8 - examples/marketputer/brands/payai.json | 19 - examples/marketputer/brands/tgmetrics.json | 51 - examples/marketputer/package.json | 52 - .../src/__tests__/orchestrator.test.ts | 235 - examples/marketputer/src/cli.ts | 17 - examples/marketputer/src/commands/run.ts | 418 - examples/marketputer/src/lib/logger.ts | 255 - examples/marketputer/src/lib/utils.ts | 61 - examples/marketputer/src/orchestrator.ts | 1465 --- examples/marketputer/src/types.ts | 124 - examples/marketputer/tsconfig.json | 18 - examples/marketputer/vitest.config.ts | 11 - package.json | 109 +- packages/cli/.gitignore | 7 - packages/cli/.npmignore | 7 - packages/cli/CHANGELOG.md | 76 - packages/cli/README.md | 325 - packages/cli/package.json | 59 - packages/cli/src/__tests__/config.test.ts | 49 - packages/cli/src/__tests__/formatting.test.ts | 72 - packages/cli/src/__tests__/wallet.test.ts | 36 - packages/cli/src/commands/agents.ts | 60 - packages/cli/src/commands/balance.ts | 93 - packages/cli/src/commands/command.ts | 257 - packages/cli/src/commands/prompt.ts | 216 - packages/cli/src/index.ts | 60 - packages/cli/src/lib/config.ts | 73 - packages/cli/src/lib/wallet.ts | 32 - packages/cli/src/utils/formatting.ts | 100 - packages/cli/tsconfig.json | 20 - packages/sdk/.npmignore | 35 - packages/sdk/CHANGELOG.md | 122 - packages/sdk/README.md | 271 - packages/sdk/package.json | 53 - packages/sdk/src/__tests__/api.test.ts | 396 - packages/sdk/src/__tests__/index.test.ts | 374 - packages/sdk/src/api.ts | 977 -- packages/sdk/src/index.ts | 332 - packages/sdk/src/utils.ts | 237 - packages/sdk/src/x402Client.ts | 415 - packages/sdk/tsconfig.json | 21 - packages/sdk/vitest.config.ts | 9 - pnpm-lock.yaml | 8721 +++-------------- pnpm-workspace.yaml | 5 - scripts/generate-base-wallet.ts | 55 - scripts/generate-solana-wallet.ts | 64 - scripts/post-build.mjs | 15 + src/agents.ts | 84 + src/cli.mts | 400 + src/client.ts | 307 + src/errors.ts | 23 + src/index.ts | 45 + src/internal/canonical.ts | 128 + src/internal/constants.ts | 42 + src/internal/error-codes.ts | 93 + src/internal/vault-pda.ts | 40 + src/internal/ws-events.ts | 30 + src/media.ts | 88 + src/mods.ts | 85 + src/rooms.ts | 679 ++ src/signer.ts | 68 + src/types.ts | 73 + src/vault/README.md | 34 + src/vault/idl.json | 931 ++ src/vault/memeputer-vault.ts | 937 ++ src/x402.ts | 61 + test/agents-methods.test.ts | 209 + test/canonical-byte-equality.test.ts | 46 + test/cli-claim-fees.test.ts | 106 + test/cli-dispatch.test.ts | 143 + test/client-headers.test.ts | 97 + test/media-methods.test.ts | 113 + test/mods-methods.test.ts | 94 + test/rooms-dryrun.test.ts | 92 + test/rooms-methods.test.ts | 189 + test/rooms.claim-fees.test.ts | 379 + test/signer-keypair.test.ts | 20 + test/subscribe.test.ts | 117 + tsconfig.json | 27 +- tsup.config.ts | 21 + .../cli/vitest.config.ts => vitest.config.ts | 4 +- 134 files changed, 7491 insertions(+), 18194 deletions(-) delete mode 100644 .changeset/README.md delete mode 100644 .changeset/config.json delete mode 100644 .changeset/fix-eip3009-clock-skew-and-fee-payer.md delete mode 100644 .eslintrc.json delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md delete mode 100755 .husky/pre-commit delete mode 100755 .husky/pre-push delete mode 100644 .nvmrc delete mode 100644 .prettierignore delete mode 100644 .prettierrc.json create mode 100644 CHANGELOG.md delete mode 100644 CONTRIBUTING.md delete mode 100644 QUICK-START.md delete mode 100644 examples/hello-world/.env.example delete mode 100644 examples/hello-world/.gitignore delete mode 100644 examples/hello-world/README.md delete mode 100644 examples/hello-world/command.ts delete mode 100644 examples/hello-world/lib/balance.ts delete mode 100644 examples/hello-world/lib/config.ts delete mode 100644 examples/hello-world/lib/payment.ts delete mode 100644 examples/hello-world/lib/wallet.ts delete mode 100644 examples/hello-world/package.json delete mode 100644 examples/hello-world/prompt.ts delete mode 100644 examples/hello-world/tsconfig.json delete mode 100644 examples/integration-test-command-formats/README.md delete mode 100644 examples/integration-test-command-formats/lib/balance.ts delete mode 100644 examples/integration-test-command-formats/lib/config.ts delete mode 100644 examples/integration-test-command-formats/lib/payment.ts delete mode 100644 examples/integration-test-command-formats/lib/wallet.ts delete mode 100644 examples/integration-test-command-formats/package.json delete mode 100644 examples/integration-test-command-formats/test-1-single-arg.ts delete mode 100644 examples/integration-test-command-formats/test-2-single-arg-quoted.ts delete mode 100644 examples/integration-test-command-formats/test-3-cli-flags.ts delete mode 100644 examples/integration-test-command-formats/test-4-json-format.ts delete mode 100644 examples/integration-test-command-formats/test-5-mixed-format.ts delete mode 100644 examples/integration-test-command-formats/tsconfig.json delete mode 100644 examples/integration-test/README.md delete mode 100644 examples/integration-test/lib/balance.ts delete mode 100644 examples/integration-test/lib/config.ts delete mode 100644 examples/integration-test/lib/payment.ts delete mode 100644 examples/integration-test/lib/wallet.ts delete mode 100644 examples/integration-test/package.json delete mode 100644 examples/integration-test/test-all.ts delete mode 100644 examples/integration-test/test.ts delete mode 100644 examples/integration-test/tsconfig.json delete mode 100644 examples/marketputer/.gitignore delete mode 100644 examples/marketputer/README.md delete mode 100644 examples/marketputer/brands/README.md delete mode 100644 examples/marketputer/brands/memeputer.json delete mode 100644 examples/marketputer/brands/payai.json delete mode 100644 examples/marketputer/brands/tgmetrics.json delete mode 100644 examples/marketputer/package.json delete mode 100644 examples/marketputer/src/__tests__/orchestrator.test.ts delete mode 100644 examples/marketputer/src/cli.ts delete mode 100644 examples/marketputer/src/commands/run.ts delete mode 100644 examples/marketputer/src/lib/logger.ts delete mode 100644 examples/marketputer/src/lib/utils.ts delete mode 100644 examples/marketputer/src/orchestrator.ts delete mode 100644 examples/marketputer/src/types.ts delete mode 100644 examples/marketputer/tsconfig.json delete mode 100644 examples/marketputer/vitest.config.ts delete mode 100644 packages/cli/.gitignore delete mode 100644 packages/cli/.npmignore delete mode 100644 packages/cli/CHANGELOG.md delete mode 100644 packages/cli/README.md delete mode 100644 packages/cli/package.json delete mode 100644 packages/cli/src/__tests__/config.test.ts delete mode 100644 packages/cli/src/__tests__/formatting.test.ts delete mode 100644 packages/cli/src/__tests__/wallet.test.ts delete mode 100644 packages/cli/src/commands/agents.ts delete mode 100644 packages/cli/src/commands/balance.ts delete mode 100644 packages/cli/src/commands/command.ts delete mode 100644 packages/cli/src/commands/prompt.ts delete mode 100644 packages/cli/src/index.ts delete mode 100644 packages/cli/src/lib/config.ts delete mode 100644 packages/cli/src/lib/wallet.ts delete mode 100644 packages/cli/src/utils/formatting.ts delete mode 100644 packages/cli/tsconfig.json delete mode 100644 packages/sdk/.npmignore delete mode 100644 packages/sdk/CHANGELOG.md delete mode 100644 packages/sdk/README.md delete mode 100644 packages/sdk/package.json delete mode 100644 packages/sdk/src/__tests__/api.test.ts delete mode 100644 packages/sdk/src/__tests__/index.test.ts delete mode 100644 packages/sdk/src/api.ts delete mode 100644 packages/sdk/src/index.ts delete mode 100644 packages/sdk/src/utils.ts delete mode 100644 packages/sdk/src/x402Client.ts delete mode 100644 packages/sdk/tsconfig.json delete mode 100644 packages/sdk/vitest.config.ts delete mode 100644 pnpm-workspace.yaml delete mode 100644 scripts/generate-base-wallet.ts delete mode 100644 scripts/generate-solana-wallet.ts create mode 100644 scripts/post-build.mjs create mode 100644 src/agents.ts create mode 100644 src/cli.mts create mode 100644 src/client.ts create mode 100644 src/errors.ts create mode 100644 src/index.ts create mode 100644 src/internal/canonical.ts create mode 100644 src/internal/constants.ts create mode 100644 src/internal/error-codes.ts create mode 100644 src/internal/vault-pda.ts create mode 100644 src/internal/ws-events.ts create mode 100644 src/media.ts create mode 100644 src/mods.ts create mode 100644 src/rooms.ts create mode 100644 src/signer.ts create mode 100644 src/types.ts create mode 100644 src/vault/README.md create mode 100644 src/vault/idl.json create mode 100644 src/vault/memeputer-vault.ts create mode 100644 src/x402.ts create mode 100644 test/agents-methods.test.ts create mode 100644 test/canonical-byte-equality.test.ts create mode 100644 test/cli-claim-fees.test.ts create mode 100644 test/cli-dispatch.test.ts create mode 100644 test/client-headers.test.ts create mode 100644 test/media-methods.test.ts create mode 100644 test/mods-methods.test.ts create mode 100644 test/rooms-dryrun.test.ts create mode 100644 test/rooms-methods.test.ts create mode 100644 test/rooms.claim-fees.test.ts create mode 100644 test/signer-keypair.test.ts create mode 100644 test/subscribe.test.ts create mode 100644 tsup.config.ts rename packages/cli/vitest.config.ts => vitest.config.ts (66%) diff --git a/.changeset/README.md b/.changeset/README.md deleted file mode 100644 index a5568bd..0000000 --- a/.changeset/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Changesets - -This directory contains changeset files that describe changes to packages in this monorepo. - -## How to use - -1. When making changes, run `pnpm changeset` to create a changeset file -2. The changeset will ask which packages changed and what type of change (major/minor/patch) -3. Commit the changeset file along with your changes -4. When ready to release, merge changesets will create a PR with version bumps -5. Merging that PR will publish packages to npm - -For more information, see the [Changesets documentation](https://github.com/changesets/changesets). - diff --git a/.changeset/config.json b/.changeset/config.json deleted file mode 100644 index d351dce..0000000 --- a/.changeset/config.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", - "changelog": "@changesets/cli/changelog", - "commit": false, - "fixed": [], - "linked": [], - "access": "public", - "baseBranch": "main", - "updateInternalDependencies": "patch", - "ignore": [] -} - diff --git a/.changeset/fix-eip3009-clock-skew-and-fee-payer.md b/.changeset/fix-eip3009-clock-skew-and-fee-payer.md deleted file mode 100644 index cfea772..0000000 --- a/.changeset/fix-eip3009-clock-skew-and-fee-payer.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@memeputer/sdk": patch ---- - -Fix EIP-3009 authorization clock skew and Solana fee payer - -- Add 60-second buffer to validAfter timestamp to account for blockchain clock skew -- Use fee payer address from 402 response instead of hardcoded value -- Update fallback fee payer to correct address (561oabzy81vXYYbs1ZHR1bvpiEr6Nbfd6PGTxPshoz4p) - diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 3b0390b..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2020, - "sourceType": "module" - }, - "plugins": ["@typescript-eslint"], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "rules": { - "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/no-explicit-any": "warn", - "no-inner-declarations": "off", - "prefer-const": "error" - }, - "ignorePatterns": ["dist", "node_modules", "*.js"] -} - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 9cf0a07..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,29 +0,0 @@ -## Description - - - -## Type of Change - -- [ ] Bug fix -- [ ] New feature -- [ ] Breaking change -- [ ] Documentation update - -## Packages Changed - -- [ ] `memeputer` (CLI) -- [ ] `@memeputer/sdk` -- [ ] `@memeputer/core` - -## Testing - - - -## Checklist - -- [ ] Code follows the project's style guidelines -- [ ] Self-review completed -- [ ] Comments added for complex code -- [ ] Documentation updated (if needed) -- [ ] Changeset added (run `pnpm changeset`) - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eeb273e..ba03a67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,33 +1,28 @@ name: CI on: - push: - branches: [main] pull_request: + push: branches: [main] jobs: - build: + test: + name: Typecheck, test, and build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v2 + + - uses: pnpm/action-setup@v4 with: - version: 8 - + version: 10.16.0 + - uses: actions/setup-node@v4 with: - node-version: 20 - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build packages - run: pnpm build - - - name: Run tests - run: pnpm test - continue-on-error: true + node-version: 22 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm typecheck + - run: pnpm test + - run: pnpm build + - run: pnpm pack:dry diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 180d1e4..f247165 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,41 +2,42 @@ name: Release on: push: - branches: - - main + tags: + - 'memeputer@*' + +permissions: + contents: read + id-token: write jobs: - release: + publish: + name: Publish memeputer to npm runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 with: - fetch-depth: 0 - - - uses: pnpm/action-setup@v2 - with: - version: 8 - + version: 10.16.0 + - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 + cache: pnpm registry-url: 'https://registry.npmjs.org' - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build packages - run: pnpm build - - - name: Create Release Pull Request or Publish - uses: changesets/action@v1 - with: - publish: pnpm changeset publish - version: pnpm changeset version - commit: 'chore: version packages' - title: 'chore: version packages' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Ensure modern npm CLI for OIDC + run: | + npm install -g npm@11.5.0 + npm --version + + - run: pnpm install --frozen-lockfile + - run: pnpm typecheck + - run: pnpm test + - run: pnpm build + - run: pnpm pack:dry + + - name: Publish to npm with provenance + env: + NPM_CONFIG_PROVENANCE: 'true' + run: npm publish --access public diff --git a/.gitignore b/.gitignore index 9db0c5e..6a2025e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,50 +1,5 @@ -# Dependencies node_modules/ -.pnp -.pnp.js - -# Build outputs dist/ -build/ -*.tsbuildinfo - -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -# OS -.DS_Store -Thumbs.db - -# IDE -.vscode/ -.idea/ -*.swp -*.swo -*~ - -# Environment +*.tgz .env -.env.local -.env.*.local - -# Testing -coverage/ -.nyc_output/ - -# Changesets -.changeset/*.md -!/.changeset/README.md - -# Temporary files -*.tmp -.cache/ - -# Marketputer runs -examples/marketputer/runs/ - +.DS_Store diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index d3956a0..0000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -# Run build and test for packages that have these scripts -echo "πŸ”¨ Running build and test before commit..." - -# Get to repo root -cd "$(git rev-parse --show-toplevel)" || exit 1 - -# Run build for SDK (the main package we're working on) -cd packages/sdk && pnpm build || { - echo "❌ SDK build failed. Commit aborted." - exit 1 -} - -# Run tests for SDK -pnpm test:run || { - echo "❌ SDK tests failed. Commit aborted." - exit 1 -} - -# Run tests for CLI -cd ../../packages/cli && pnpm test:run || { - echo "❌ CLI tests failed. Commit aborted." - exit 1 -} - -# Run tests for marketputer example -cd ../../examples/marketputer && pnpm test:run || { - echo "❌ Marketputer tests failed. Commit aborted." - exit 1 -} - -echo "βœ… Build and test passed!" diff --git a/.husky/pre-push b/.husky/pre-push deleted file mode 100755 index a5c1641..0000000 --- a/.husky/pre-push +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -# Run build and test before push as a final check -echo "πŸ”¨ Running final build and test check before push..." - -# Get to repo root -cd "$(git rev-parse --show-toplevel)" || exit 1 - -# Run build for SDK (the main package we're working on) -cd packages/sdk && pnpm build || { - echo "❌ SDK build failed. Push aborted." - exit 1 -} - -# Run tests for SDK -pnpm test:run || { - echo "❌ SDK tests failed. Push aborted." - exit 1 -} - -# Run tests for CLI -cd ../../packages/cli && pnpm test:run || { - echo "❌ CLI tests failed. Push aborted." - exit 1 -} - -# Run tests for marketputer example -cd ../../examples/marketputer && pnpm test:run || { - echo "❌ Marketputer tests failed. Push aborted." - exit 1 -} - -echo "βœ… All checks passed! Pushing..." - diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 35f4978..0000000 --- a/.nvmrc +++ /dev/null @@ -1,2 +0,0 @@ -20 - diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 526b921..0000000 --- a/.prettierignore +++ /dev/null @@ -1,11 +0,0 @@ -node_modules -dist -build -coverage -*.lock -pnpm-lock.yaml -package-lock.json -yarn.lock -.changeset/*.md -!.changeset/README.md - diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 053c69d..0000000 --- a/.prettierrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "semi": true, - "trailingComma": "es5", - "singleQuote": true, - "printWidth": 100, - "tabWidth": 2, - "useTabs": false -} - diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6d7f754 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# memeputer + +## 2.0.0 + +### Major Changes + +- Replace the legacy Memeputer CLI package with the v2 TypeScript SDK and CLI for the agent chat platform. +- Add canonical signed-request helpers, room/agent/mod namespaces, creator-fee claim helpers, and durable media upload helpers. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 13aa009..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,114 +0,0 @@ -# Contributing to Memeputer - -Thank you for your interest in contributing to Memeputer! πŸŽ‰ - -## Development Setup - -1. **Clone the repository** - ```bash - git clone https://github.com/memeputer/memeputer-oss.git - cd memeputer-oss - ``` - -2. **Install pnpm** (if not already installed) - ```bash - npm install -g pnpm - ``` - -3. **Install dependencies** - ```bash - pnpm install - ``` - -4. **Build all packages** - ```bash - pnpm build - ``` - -## Making Changes - -1. **Create a branch** - ```bash - git checkout -b feature/your-feature-name - ``` - -2. **Make your changes** - - Write code following the existing style - - Add tests if applicable - - Update documentation as needed - -3. **Add a changeset** - ```bash - pnpm changeset - ``` - This will prompt you to describe your changes and select which packages are affected. - -4. **Commit your changes** - ```bash - git add . - git commit -m "feat: your change description" - ``` - -5. **Push and create a PR** - ```bash - git push origin feature/your-feature-name - ``` - -## Package-Specific Development - -### Working on the CLI - -```bash -# Build the CLI -pnpm --filter memeputer build - -# Watch mode -pnpm --filter memeputer dev - -# Test locally (after building) -node packages/cli/dist/cli.js --help -``` - -### Working on the SDK - -```bash -# Build the SDK -pnpm --filter @memeputer/sdk build - -# Watch mode -pnpm --filter @memeputer/sdk dev -``` - -## Code Style - -- Use TypeScript for all new code -- Follow the existing code style -- Run `pnpm lint` before committing (when linting is set up) -- Use meaningful variable and function names - -## Commit Messages - -We follow [Conventional Commits](https://www.conventionalcommits.org/): - -- `feat:` - New feature -- `fix:` - Bug fix -- `docs:` - Documentation changes -- `style:` - Code style changes (formatting, etc.) -- `refactor:` - Code refactoring -- `test:` - Adding or updating tests -- `chore:` - Maintenance tasks - -## Testing - -```bash -# Run all tests -pnpm test - -# Run tests for a specific package -pnpm --filter memeputer test -``` - -## Questions? - -Feel free to open an issue or reach out to the maintainers! - diff --git a/QUICK-START.md b/QUICK-START.md deleted file mode 100644 index 7753b7f..0000000 --- a/QUICK-START.md +++ /dev/null @@ -1,97 +0,0 @@ -# Quick Start Guide - -The fastest way to get started with Memeputer on Solana or Base. - -## 1. Install Dependencies - -```bash -pnpm install -``` - -## 2. Generate a Wallet - -Choose your blockchain: - -```bash -# Generate a Solana wallet (saves to ~/.config/solana/id.json) -pnpm run generate-solana-wallet - -# OR generate a Base wallet (saves to ~/.memeputer/base-wallet.json) -pnpm run generate-base-wallet -``` - -## 3. Fund Your Wallet - -### For Solana: -- **Devnet (testing):** `solana airdrop 1 YOUR_ADDRESS --url devnet` -- **Mainnet:** Buy SOL on any exchange, send to your address, then swap for USDC on [Jupiter](https://jup.ag) - -### For Base: -- **Testnet:** Get Base Sepolia ETH from [Alchemy Faucet](https://www.alchemy.com/faucets/base-sepolia) -- **Mainnet:** Bridge funds to Base using [Base Bridge](https://bridge.base.org) - -## 4. Run Hello World Example - -```bash -cd examples/hello-world - -# Run on Solana (default) -export MEMEPUTER_CHAIN=solana -pnpm run prompt "What's the weather?" - -# OR run on Base -export MEMEPUTER_CHAIN=base -pnpm run prompt "What's the weather?" -``` - -## 5. Switch Chains Anytime - -Just change one environment variable: - -```bash -# Solana -export MEMEPUTER_CHAIN=solana -export MEMEPUTER_API_URL=http://localhost:3007/x402 - -# Base -export MEMEPUTER_CHAIN=base -export MEMEPUTER_API_URL=http://localhost:3007/x402 -``` - -That's it! The SDK handles everything else automatically. - -## Configuration Options - -### Environment Variables - -- `MEMEPUTER_API_URL` - API endpoint (default: `https://agents.memeputer.com/x402`) -- `MEMEPUTER_CHAIN` - Blockchain: `solana` (default) or `base` -- `MEMEPUTER_WALLET` - Path to wallet file (Solana) -- `MEMEPUTER_WALLET_PRIVATE_KEY` - Private key (Base/EVM) - -### Config File (~/.memeputerrc) - -```json -{ - "apiUrl": "https://agents.memeputer.com/x402", - "chain": "solana", - "wallet": "~/.config/solana/id.json" -} -``` - -## What Happens Next? - -1. Your app calls an agent (e.g., `memeputer`) -2. Server returns `402 Payment Required` with payment details -3. SDK creates and signs a USDC payment transaction -4. Payment sent via **PayAI Facilitator** (you pay $0 gas!) -5. Server processes request and returns AI response - -All payments are on-chain and instant. No accounts, no subscriptions. - -## Need Help? - -- **Documentation:** [packages/cli/README.md](./packages/cli/README.md) -- **Examples:** [examples/](./examples/) -- **Issues:** [GitHub Issues](https://github.com/rawgroundbeef/memeputer/issues) - diff --git a/README.md b/README.md index c116628..b3a2203 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,56 @@ -# Memeputer Monorepo +# memeputer -Public monorepo for all Memeputer packages. - -## Packages - -- **[`memeputer`](./packages/cli)** - CLI for interacting with Memeputer agents - -## Getting Started - -### Prerequisites - -- Node.js >= 18.0.0 -- pnpm >= 8.0.0 - -### Installation - -```bash -# Install pnpm if you haven't already -npm install -g pnpm - -# Install dependencies -pnpm install -``` - -### Quick Wallet Setup - -Generate a wallet for testing: +Official TypeScript SDK + CLI for the [Memeputer](https://memeputer.com) agent +chat platform. ```bash -# Generate a Solana wallet (saves to ~/.config/solana/id.json) -pnpm run generate-solana-wallet - -# OR generate a Base wallet (saves to ~/.memeputer/base-wallet.json) -pnpm run generate-base-wallet +npm install memeputer @solana/web3.js @openfacilitator/sdk +# Optional β€” only required if you want live WebSocket subscribe: +npm install socket.io-client ``` -Then fund your wallet with USDC to start using Memeputer agents! +`@solana/web3.js`, `@openfacilitator/sdk`, and `socket.io-client` are **peer +dependencies** so the consumer's app and the SDK share one copy of each +on the same tree. -### Multi-Chain Support +## Quickstart -Switch between Solana and Base easily: +```ts +import { Memeputer, keypairSigner } from 'memeputer'; +import { Keypair } from '@solana/web3.js'; -```bash -# Use Solana (default) -export MEMEPUTER_CHAIN=solana -pnpm --filter hello-world start +const mp = new Memeputer({ + signer: keypairSigner(Keypair.generate()), + apiUrl: process.env.MEMEPUTER_API_URL ?? 'https://api-production-651b.up.railway.app', +}); -# Use Base -export MEMEPUTER_CHAIN=base -pnpm --filter hello-world start +// Public read β€” no signing. +const rooms = await mp.rooms.list({ sort: 'mcap', limit: 10 }); ``` -See the [CLI README](./packages/cli/README.md) for more details. +## Media -### Development - -```bash -# Build all packages -pnpm build +Agents can bring their own optimized WebP avatars. The SDK signs the media +request, uploads the bytes to Memeputer's durable media bucket, and returns the +public `https://media.memeputer.com/...` URL. -# Build a specific package -pnpm --filter memeputer build - -# Run tests -pnpm test - -# Watch mode for development -pnpm --filter memeputer dev -``` - -## Project Structure - -``` -memeputer/ -β”œβ”€β”€ packages/ -β”‚ └── cli/ # memeputer CLI -β”œβ”€β”€ examples/ # Example applications -β”‚ └── marketputer/ # Marketputer example app -β”œβ”€β”€ .github/ # GitHub Actions workflows -└── .changeset/ # Changesets for versioning +```ts +const { publicUrl } = await mp.media.uploadAgentAvatar(webpBytes); +await mp.agents.patch(mp.signer.publicKey.toBase58(), { avatarUrl: publicUrl }); ``` -## Versioning & Publishing - -This monorepo uses [Changesets](https://github.com/changesets/changesets) for versioning and publishing. - -### Making Changes - -1. Make your changes to the relevant package(s) -2. Run `pnpm changeset` to create a changeset file -3. Commit your changes and the changeset file -4. Push to `main` branch - -### Publishing - -When changesets are merged to `main`, a GitHub Action will: -1. Create a PR with version bumps -2. After merging that PR, automatically publish packages to npm - -### Manual Publishing +## CLI ```bash -# Version packages -pnpm changeset version - -# Publish to npm -pnpm changeset publish +npx memeputer rooms list --sort newest --limit 20 ``` -## Contributing +Run `memeputer --help` for the full sub-command tree. + +## Documentation -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Add a changeset (`pnpm changeset`) -5. Submit a pull request +Full reference, error codes, and concept docs: ## License MIT - diff --git a/examples/hello-world/.env.example b/examples/hello-world/.env.example deleted file mode 100644 index 46d4a08..0000000 --- a/examples/hello-world/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -# Optional: Wallet path (defaults to ~/.config/solana/id.json) -# MEMEPUTER_WALLET=~/.config/solana/id.json - -# Optional: Solana RPC URL (defaults to mainnet) -# SOLANA_RPC_URL=https://api.mainnet-beta.solana.com diff --git a/examples/hello-world/.gitignore b/examples/hello-world/.gitignore deleted file mode 100644 index 8530f70..0000000 --- a/examples/hello-world/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/ -wallet.json -.env -*.log -.DS_Store - diff --git a/examples/hello-world/README.md b/examples/hello-world/README.md deleted file mode 100644 index 1c571c5..0000000 --- a/examples/hello-world/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# Hello World - Minimal Example - -The **absolute minimum** code needed to call a Memeputer AI agent via x402 micropayments. - -## What This Does - -1. Loads your Solana wallet -2. Connects to Solana network -3. Calls the `memeputer` agent with "Hello" -4. Shows the response -5. Costs ~$0.01 USDC (one cent) - -## Quick Start - -### 1. Install Dependencies - -```bash -pnpm install -``` - -### 2. Configure Environment (Optional) - -The example will automatically use your default Solana CLI wallet at `~/.config/solana/id.json` if you don't set `MEMEPUTER_WALLET`. - -To use a different wallet, copy the example environment file: - -```bash -cp .env.example .env -``` - -Then edit `.env` and set `MEMEPUTER_WALLET` to your wallet file path: - -```bash -# Use default Solana CLI wallet (leave empty) -MEMEPUTER_WALLET= - -# Or specify a custom path -MEMEPUTER_WALLET=./wallet.json -# or -MEMEPUTER_WALLET=~/.config/solana/id.json -``` - -### 3. Set Up Your Wallet - -You need a wallet with USDC. **Quickest way:** - -```bash -# Generate a Solana wallet (saves to ~/.config/solana/id.json) -cd ../.. # Go to project root -pnpm run generate-solana-wallet - -# OR generate a Base wallet (saves to ~/.memeputer/base-wallet.json) -pnpm run generate-base-wallet -``` - -Then fund your wallet with USDC! - -**Other options:** - -**Option A: Use existing Phantom wallet** -```bash -# Export your Phantom wallet: -# 1. Open Phantom browser extension -# 2. Settings β†’ Export Private Key -# 3. Save as wallet.json in this directory -``` - -**Option B: Create new wallet with Solana CLI** -```bash -# Install Solana CLI (if needed) -sh -c "$(curl -sSfL https://release.solana.com/stable/install)" - -# Generate new wallet -solana-keygen new --outfile wallet.json - -# Fund with USDC (get address with: solana address -k wallet.json) -``` - -### 4. Run the Example - -```bash -pnpm start -``` - -That's it! The example will: -- Load your wallet -- Call the agent -- Show the response -- Payment happens automatically via x402 - -## Common Wallet Locations - -Solana wallets are typically stored in these locations: - -- **Solana CLI default**: `~/.config/solana/id.json` (used automatically if no `MEMEPUTER_WALLET` is set) -- **Local project wallet**: `./wallet.json` (created in current directory) -- **Phantom exported**: `./phantom-wallet.json` (after exporting from Phantom) - -## Customization - -Edit `.env` file to customize: - -```bash -# Use default Solana CLI wallet (leave empty) -MEMEPUTER_WALLET= - -# Or specify custom wallet path -MEMEPUTER_WALLET=./wallet.json - -# Change agent -MEMEPUTER_AGENT_ID=memeputer - -# Change message -MEMEPUTER_MESSAGE=Hello -``` - -All configuration is done via environment variables (loaded from `.env` file). - -## How It Works - -The code follows this simple flow: - -1. **Load wallet** - Reads your Solana keypair from file -2. **Connect** - Creates connection to Solana network -3. **Create client** - Initializes the API client -4. **Call agent** - `client.interact()` handles the entire x402 payment flow: - - Makes initial request (gets 402 Payment Required) - - Creates USDC payment transaction - - Signs with your wallet - - Sends payment via PayAI Facilitator ($0 gas fees!) - - Gets AI response - -All payment logic is handled automatically by the `AgentsApiClient`. - -## Code Breakdown - -The main file (`prompt.ts`) is now super clean: - -```typescript -// Load wallet -const wallet = loadWallet(WALLET_PATH); - -// Connect to Solana -const connection = new Connection(RPC_URL, "confirmed"); - -// Create client -const client = new AgentsApiClient(API_URL); - -// Call agent (payment happens automatically!) -const result = await client.interact(AGENT_ID, MESSAGE, wallet, connection); -``` - -**Total: ~4 lines of core logic** - wallet loading, connection, client creation, and agent call. - -Utility functions (path expansion, wallet loading) are in `lib/wallet.ts` to keep the main file minimal. - -## Next Steps - -- See `../marketputer/` for a full-featured example -- Check `packages/cli/` for the command-line tool -- Read the [main README](../../README.md) for more info - -## Troubleshooting - -**Error: Wallet file not found** -- The example defaults to `~/.config/solana/id.json` (standard Solana CLI location) -- If you don't have a wallet there, either: - - Create one: `solana-keygen new` (saves to default location) - - Or set `MEMEPUTER_WALLET` in `.env` to point to your wallet file -- Common locations: - - `~/.config/solana/id.json` (Solana CLI default) - - `./wallet.json` (local project wallet) - -**Error: Insufficient USDC balance** -- Your wallet needs USDC (not SOL) -- Add USDC to your wallet address - -**Error: Payment required** -- Check your USDC balance -- Ensure wallet has enough for the transaction (~$0.01 USDC) - diff --git a/examples/hello-world/command.ts b/examples/hello-world/command.ts deleted file mode 100644 index 1c12172..0000000 --- a/examples/hello-world/command.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Minimal "Command" example - demonstrates using commands - * - * This shows the simplest way to execute a command on an agent. - * - * Usage: - * pnpm command [agent] - * pnpm command rawgroundbeefbot - */ - -import memeputer from "@memeputer/sdk"; -import { loadConfig } from "./lib/config"; -import { showPaymentDetails } from "./lib/payment"; -import { loadWallet } from "./lib/wallet"; -import { checkBalance } from "./lib/balance"; -import { Connection } from "@solana/web3.js"; - -// Load configuration -const config = loadConfig(); - -// Enable verbose logging to see x402 protocol details -memeputer.enableVerbose(); - -async function main() { - // Check wallet balance - const wallet = loadWallet(config.walletPath); - const connection = new Connection(config.rpcUrl, "confirmed"); - await checkBalance(wallet, connection); - - // Execute ping command (same as: memeputer command memeputer ping) - const agentId = process.argv[2] || config.agentId; - console.log(`you: /ping`); - - // Use command method (matches prompt signature: command(agentId, commandName, params?)) - // Same as: memeputer command memeputer ping - const result = await memeputer.command(agentId, "ping"); - - // Show response - if (result.response && result.response.trim()) { - console.log(`\n${agentId}: "${result.response}"`); - } else { - console.log(`\n${agentId}: (command executed, but no text response)`); - console.log(` Note: Some commands may not return text responses.`); - } - - // Show payment details - showPaymentDetails(result); -} - -main().catch((error) => { - console.error("❌ Error:", error.message); - process.exit(1); -}); - diff --git a/examples/hello-world/lib/balance.ts b/examples/hello-world/lib/balance.ts deleted file mode 100644 index f8ea3d5..0000000 --- a/examples/hello-world/lib/balance.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Connection, Keypair } from "@solana/web3.js"; -import { getUsdcBalance } from "@memeputer/sdk"; - -/** - * Check wallet balance and exit if insufficient - */ -export async function checkBalance( - wallet: Keypair, - connection: Connection -): Promise { - console.log("πŸ’° Checking wallet balance..."); - - const balance = await getUsdcBalance(connection, wallet); - console.log(` Current balance: ${balance.toFixed(4)} USDC`); - console.log(` Wallet: ${wallet.publicKey.toBase58()}\n`); - - if (balance === 0) { - console.error("❌ Error: Insufficient USDC balance"); - console.error(` Your wallet has 0 USDC. Please fund your wallet with USDC to continue.`); - console.error(` Wallet address: ${wallet.publicKey.toBase58()}`); - process.exit(1); - } - - if (balance < 0.01) { - console.warn(`⚠️ Warning: Low balance (${balance.toFixed(4)} USDC)`); - console.warn(` Typical agent interactions cost 0.01-0.10 USDC`); - console.warn(` You may not have enough for this interaction.\n`); - } -} - diff --git a/examples/hello-world/lib/config.ts b/examples/hello-world/lib/config.ts deleted file mode 100644 index 25053ef..0000000 --- a/examples/hello-world/lib/config.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { config } from "dotenv"; -import { getDefaultWalletPath } from "./wallet"; - -// Load environment variables from .env file -config(); - -export interface Config { - apiUrl: string; - rpcUrl: string; - walletPath: string; - agentId: string; - message: string; - chain: string; // Blockchain: 'solana' (default) | 'base' -} - -/** - * Load configuration from environment variables with sensible defaults - */ -export function loadConfig(): Config { - return { - apiUrl: process.env.MEMEPUTER_API_URL || "https://agents.memeputer.com/x402", - rpcUrl: process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com", - walletPath: process.env.MEMEPUTER_WALLET || getDefaultWalletPath(), - agentId: process.env.MEMEPUTER_AGENT_ID || "memeputer", - message: process.env.MEMEPUTER_MESSAGE || "Hello", - chain: process.env.MEMEPUTER_CHAIN || "solana", // Default to Solana - }; -} - diff --git a/examples/hello-world/lib/payment.ts b/examples/hello-world/lib/payment.ts deleted file mode 100644 index 6d6e572..0000000 --- a/examples/hello-world/lib/payment.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { InteractionResult } from "@memeputer/sdk"; - -/** - * Detect if a transaction signature is Base/EVM (hex starting with 0x) or Solana (base58) - */ -function isBaseTransaction(signature: string): boolean { - if (!signature) return false; - // Base transaction hashes are hex strings starting with 0x and exactly 66 chars (0x + 64 hex chars) - return signature.startsWith('0x') && - signature.length === 66 && - /^0x[a-fA-F0-9]{64}$/.test(signature); -} - -/** - * Get transaction URL - automatically detects Base vs Solana - */ -function getTxUrl(signature: string): string { - if (isBaseTransaction(signature)) { - return `https://basescan.org/tx/${signature}`; - } - return `https://solscan.io/tx/${signature}`; -} - -/** - * Get account URL - automatically detects Base (0x...) vs Solana (base58) - */ -function getAccountUrl(address: string): string { - if (address.startsWith('0x') && address.length === 42) { - // Base/EVM address - return `https://basescan.org/address/${address}`; - } - // Solana address - return `https://solscan.io/account/${address}`; -} - -/** - * Display payment details in a clean format - */ -export function showPaymentDetails(result: InteractionResult) { - if (!result.transactionSignature) { - return; - } - - const txUrl = getTxUrl(result.transactionSignature); - console.log(`\nπŸ’Έ Payment Details:\n`); - console.log(`Transaction: ${txUrl}\n`); - - if (result.x402Receipt) { - const receipt = result.x402Receipt; - console.log(`Amount: ${receipt.amountPaidUsdc.toFixed(4)} USDC\n`); - - if (receipt.payer && receipt.payer !== 'unknown') { - const payerUrl = getAccountUrl(receipt.payer); - console.log(`From: ${receipt.payer}`); - console.log(`${payerUrl}\n`); - } - - if (receipt.merchant || receipt.payTo) { - const merchant = receipt.merchant || receipt.payTo; - const merchantUrl = getAccountUrl(merchant); - console.log(`To: ${merchant}`); - console.log(`${merchantUrl}\n`); - } - } else if (result.x402Quote) { - console.log(`Amount: ${result.x402Quote.amountQuotedUsdc.toFixed(4)} USDC\n`); - } -} - diff --git a/examples/hello-world/lib/wallet.ts b/examples/hello-world/lib/wallet.ts deleted file mode 100644 index 72b235c..0000000 --- a/examples/hello-world/lib/wallet.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { readFileSync, existsSync } from "fs"; -import { join } from "path"; -import { homedir } from "os"; -import { Keypair } from "@solana/web3.js"; -import bs58 from "bs58"; - -/** - * Get default Solana wallet path (standard location) - */ -export function getDefaultWalletPath(): string { - return join(homedir(), ".config", "solana", "id.json"); -} - -/** - * Expand ~ to home directory (Node.js doesn't do this automatically) - */ -export function expandPath(path: string): string { - if (path.startsWith("~/")) { - return join(homedir(), path.slice(2)); - } - if (path === "~") { - return homedir(); - } - if (path.startsWith("~")) { - // Handle ~username format (though we'll just expand to current user's home) - return join(homedir(), path.slice(1)); - } - return path; -} - -/** - * Load wallet from file path (handles both array and base58 formats) - */ -export function loadWallet(walletPath: string): Keypair { - const expandedPath = expandPath(walletPath); - - if (!existsSync(expandedPath)) { - throw new Error(`Wallet file not found: ${expandedPath}`); - } - - const walletData = JSON.parse(readFileSync(expandedPath, "utf-8")); - - if (Array.isArray(walletData)) { - return Keypair.fromSecretKey(Uint8Array.from(walletData)); - } else if (typeof walletData === "string") { - return Keypair.fromSecretKey(bs58.decode(walletData)); - } else { - throw new Error("Invalid wallet format"); - } -} - - diff --git a/examples/hello-world/package.json b/examples/hello-world/package.json deleted file mode 100644 index 7ecc45f..0000000 --- a/examples/hello-world/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "hello-world", - "version": "1.1.0", - "private": true, - "description": "Minimal example for calling Memeputer agents via x402", - "type": "module", - "main": "prompt.ts", - "scripts": { - "start": "tsx prompt.ts \"hello\"", - "prompt": "tsx prompt.ts", - "command": "tsx command.ts" - }, - "dependencies": { - "@solana/web3.js": "^1.98.4", - "@memeputer/sdk": "^1.8.0", - "bs58": "^6.0.0", - "dotenv": "^16.4.5" - }, - "devDependencies": { - "@types/node": "^20.11.30", - "tsx": "^4.16.0", - "typescript": "^5.6.3" - } -} - diff --git a/examples/hello-world/prompt.ts b/examples/hello-world/prompt.ts deleted file mode 100644 index c9bad3e..0000000 --- a/examples/hello-world/prompt.ts +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Minimal "Hello World" example for Memeputer Agent API - * - * This demonstrates the absolute minimum code needed to call an AI agent - * via x402 micropayments on Solana. - * - * Usage: - * 1. Install dependencies: pnpm install - * 2. Set up your wallet (see README.md) - * 3. Run: pnpm start - */ - -import { Memeputer } from "@memeputer/sdk"; -import { loadConfig } from "./lib/config"; -import { showPaymentDetails } from "./lib/payment"; -import { loadWallet } from "./lib/wallet"; -import { checkBalance } from "./lib/balance"; -import { Connection } from "@solana/web3.js"; - -// Load configuration -const config = loadConfig(); - -// Get message from command line argument or use config default -const message = process.argv[2] || config.message; - -async function main() { - console.log(`πŸ”— Chain: ${config.chain}`); - console.log(`you: "${message}"\n`); - - let wallet; - let connection; - - if (config.chain === 'solana') { - // Step 1: Load Solana wallet and check balance - wallet = loadWallet(config.walletPath); - connection = new Connection(config.rpcUrl, "confirmed"); - - console.log('πŸ’° Checking wallet balance...'); - await checkBalance(wallet, connection); - } else if (config.chain === 'base') { - // Step 1: Load Base/EVM wallet - const privateKey = process.env.MEMEPUTER_WALLET_PRIVATE_KEY; - - if (!privateKey) { - throw new Error( - 'Base wallet not configured!\n' + - ' Set MEMEPUTER_WALLET_PRIVATE_KEY in your .env file\n' + - ' Run: pnpm run generate-base-wallet (from project root)' - ); - } - - // Derive wallet address from private key to show user - const { ethers } = await import('ethers'); - const evmWallet = new ethers.Wallet(privateKey); - - wallet = { privateKey }; - connection = null; // Base uses different provider - - console.log('πŸ’° Using Base wallet'); - console.log(' Your Address:', evmWallet.address); - console.log(' ⚠️ Make sure THIS address has USDC, not the payment recipient!'); - } else { - throw new Error(`Unsupported chain: ${config.chain}. Use 'solana' or 'base'`); - } - - // Step 2: Create Memeputer client with chain configuration - const memeputer = new Memeputer({ - apiUrl: config.apiUrl, - chain: config.chain, - wallet, - connection, - verbose: true, // Enable verbose logging to see x402 protocol details - }); - - // Step 3: Prompt agent - Payment happens automatically via x402 - const result = await memeputer.prompt(config.agentId, message); - - // Step 4: Show agent response - console.log(`\n${config.agentId}: "${result.response}"`); - - // Step 5: Show payment details - showPaymentDetails(result); -} - -main().catch((error) => { - console.error("❌ Error:", error.message); - process.exit(1); -}); diff --git a/examples/hello-world/tsconfig.json b/examples/hello-world/tsconfig.json deleted file mode 100644 index c32ee27..0000000 --- a/examples/hello-world/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "lib": ["ES2022"], - "moduleResolution": "bundler", - "esModuleInterop": true, - "skipLibCheck": true, - "strict": true, - "resolveJsonModule": true, - "types": ["node"] - }, - "include": ["*.ts"], - "exclude": ["node_modules"] -} - diff --git a/examples/integration-test-command-formats/README.md b/examples/integration-test-command-formats/README.md deleted file mode 100644 index 222b7a3..0000000 --- a/examples/integration-test-command-formats/README.md +++ /dev/null @@ -1,142 +0,0 @@ -# Integration Test - Command Formats - -Integration testing suite for command format parsing. Tests various input formats (CLI strings vs JSON) to ensure the backend correctly parses different command formats that users might input. - -## Purpose - -These scripts test whether the backend can parse different CLI format strings that users might type in x402scan's single text box interface. This ensures the `enhance_prompt` command works regardless of how users format their input. - -## Setup - -1. Install dependencies: - ```bash - cd examples/integration-test-command-formats - pnpm install - ``` - -2. Configure your wallet (same as hello-world example): - - Set `MEMEPUTER_WALLET` environment variable, or - - Place your wallet at `~/.config/solana/id.json` - -3. Set API URL (optional): - ```bash - export MEMEPUTER_API_URL="https://agents.memeputer.com/x402" - ``` - -## Test Scripts - -### Test 1: Single Argument (No Quotes) -```bash -pnpm test:1 -``` -**Tests:** `/enhance_prompt a cyberpunk samurai` - -Verifies that a simple positional argument without quotes is parsed correctly as `basePrompt`. - ---- - -### Test 2: Single Argument (With Quotes) -```bash -pnpm test:2 -``` -**Tests:** `/enhance_prompt "a futuristic cityscape at sunset"` - -Verifies that quoted positional arguments are parsed correctly and quotes are stripped. - ---- - -### Test 3: Multiple Arguments (CLI Flags) -```bash -pnpm test:3 -``` -**Tests:** `/enhance_prompt --basePrompt="a cat wearing sunglasses" --style="artistic" --detailLevel="high"` - -Verifies that CLI flags in `--flag=value` format are parsed correctly. - ---- - -### Test 4: JSON Format (SDK Method) -```bash -pnpm test:4 -``` -**Tests:** Using SDK's `command()` method with structured parameters - -This is the standard format that the SDK uses. This should always work. - ---- - -### Test 5: Mixed Format (Positional + Flags) -```bash -pnpm test:5 -``` -**Tests:** `/enhance_prompt a peaceful landscape --style="minimalist" --tone="serene"` - -Verifies that mixed format (positional argument + flags) is handled correctly. - ---- - -## Run All Tests - -```bash -pnpm test:all -``` - -This runs all test scripts sequentially. - -## Expected Results - -### βœ… Should Work -- **Test 4** (JSON format via SDK) - This is the standard format -- Any test that sends JSON string directly - -### ❓ Depends on Backend Support -- **Test 1-3, 5** (CLI format strings) - Requires backend to parse CLI format and convert to JSON - -## What Each Test Does - -Each test script: -1. Loads wallet and checks balance -2. Creates a Memeputer SDK instance -3. Sends the command in the specified format -4. Displays the response -5. Attempts to parse response as JSON -6. Shows payment details - -## Understanding the Results - -### Success Indicators -- βœ… Response received without errors -- βœ… Response is valid JSON -- βœ… Response contains `enhancedPrompt` field -- βœ… Enhanced prompt is actually enhanced (longer/more detailed than input) - -### Failure Indicators -- ❌ Error parsing command -- ❌ Error response from backend -- ❌ Response is not JSON when expected -- ❌ Missing `enhancedPrompt` field - -## Notes - -- Each test costs ~$0.05 USDC (check current pricing) -- Tests use verbose logging to show the exact HTTP request/response -- All tests use the `promptputer` agent -- CLI format tests use `prompt()` method to send raw strings -- JSON format test uses `command()` method (standard SDK usage) - -## Troubleshooting - -### "No private key found" -- Make sure your wallet is configured (see Setup) - -### "Insufficient balance" -- Add USDC to your Solana wallet - -### "Command not found" or parsing errors -- The backend may not support CLI format parsing yet -- Test 4 (JSON format) should always work - -### Response is not JSON -- Some backends may return plain text instead of JSON -- Check if the response contains the enhanced prompt anyway - diff --git a/examples/integration-test-command-formats/lib/balance.ts b/examples/integration-test-command-formats/lib/balance.ts deleted file mode 100644 index f8ea3d5..0000000 --- a/examples/integration-test-command-formats/lib/balance.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Connection, Keypair } from "@solana/web3.js"; -import { getUsdcBalance } from "@memeputer/sdk"; - -/** - * Check wallet balance and exit if insufficient - */ -export async function checkBalance( - wallet: Keypair, - connection: Connection -): Promise { - console.log("πŸ’° Checking wallet balance..."); - - const balance = await getUsdcBalance(connection, wallet); - console.log(` Current balance: ${balance.toFixed(4)} USDC`); - console.log(` Wallet: ${wallet.publicKey.toBase58()}\n`); - - if (balance === 0) { - console.error("❌ Error: Insufficient USDC balance"); - console.error(` Your wallet has 0 USDC. Please fund your wallet with USDC to continue.`); - console.error(` Wallet address: ${wallet.publicKey.toBase58()}`); - process.exit(1); - } - - if (balance < 0.01) { - console.warn(`⚠️ Warning: Low balance (${balance.toFixed(4)} USDC)`); - console.warn(` Typical agent interactions cost 0.01-0.10 USDC`); - console.warn(` You may not have enough for this interaction.\n`); - } -} - diff --git a/examples/integration-test-command-formats/lib/config.ts b/examples/integration-test-command-formats/lib/config.ts deleted file mode 100644 index 25053ef..0000000 --- a/examples/integration-test-command-formats/lib/config.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { config } from "dotenv"; -import { getDefaultWalletPath } from "./wallet"; - -// Load environment variables from .env file -config(); - -export interface Config { - apiUrl: string; - rpcUrl: string; - walletPath: string; - agentId: string; - message: string; - chain: string; // Blockchain: 'solana' (default) | 'base' -} - -/** - * Load configuration from environment variables with sensible defaults - */ -export function loadConfig(): Config { - return { - apiUrl: process.env.MEMEPUTER_API_URL || "https://agents.memeputer.com/x402", - rpcUrl: process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com", - walletPath: process.env.MEMEPUTER_WALLET || getDefaultWalletPath(), - agentId: process.env.MEMEPUTER_AGENT_ID || "memeputer", - message: process.env.MEMEPUTER_MESSAGE || "Hello", - chain: process.env.MEMEPUTER_CHAIN || "solana", // Default to Solana - }; -} - diff --git a/examples/integration-test-command-formats/lib/payment.ts b/examples/integration-test-command-formats/lib/payment.ts deleted file mode 100644 index 6d6e572..0000000 --- a/examples/integration-test-command-formats/lib/payment.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { InteractionResult } from "@memeputer/sdk"; - -/** - * Detect if a transaction signature is Base/EVM (hex starting with 0x) or Solana (base58) - */ -function isBaseTransaction(signature: string): boolean { - if (!signature) return false; - // Base transaction hashes are hex strings starting with 0x and exactly 66 chars (0x + 64 hex chars) - return signature.startsWith('0x') && - signature.length === 66 && - /^0x[a-fA-F0-9]{64}$/.test(signature); -} - -/** - * Get transaction URL - automatically detects Base vs Solana - */ -function getTxUrl(signature: string): string { - if (isBaseTransaction(signature)) { - return `https://basescan.org/tx/${signature}`; - } - return `https://solscan.io/tx/${signature}`; -} - -/** - * Get account URL - automatically detects Base (0x...) vs Solana (base58) - */ -function getAccountUrl(address: string): string { - if (address.startsWith('0x') && address.length === 42) { - // Base/EVM address - return `https://basescan.org/address/${address}`; - } - // Solana address - return `https://solscan.io/account/${address}`; -} - -/** - * Display payment details in a clean format - */ -export function showPaymentDetails(result: InteractionResult) { - if (!result.transactionSignature) { - return; - } - - const txUrl = getTxUrl(result.transactionSignature); - console.log(`\nπŸ’Έ Payment Details:\n`); - console.log(`Transaction: ${txUrl}\n`); - - if (result.x402Receipt) { - const receipt = result.x402Receipt; - console.log(`Amount: ${receipt.amountPaidUsdc.toFixed(4)} USDC\n`); - - if (receipt.payer && receipt.payer !== 'unknown') { - const payerUrl = getAccountUrl(receipt.payer); - console.log(`From: ${receipt.payer}`); - console.log(`${payerUrl}\n`); - } - - if (receipt.merchant || receipt.payTo) { - const merchant = receipt.merchant || receipt.payTo; - const merchantUrl = getAccountUrl(merchant); - console.log(`To: ${merchant}`); - console.log(`${merchantUrl}\n`); - } - } else if (result.x402Quote) { - console.log(`Amount: ${result.x402Quote.amountQuotedUsdc.toFixed(4)} USDC\n`); - } -} - diff --git a/examples/integration-test-command-formats/lib/wallet.ts b/examples/integration-test-command-formats/lib/wallet.ts deleted file mode 100644 index 72b235c..0000000 --- a/examples/integration-test-command-formats/lib/wallet.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { readFileSync, existsSync } from "fs"; -import { join } from "path"; -import { homedir } from "os"; -import { Keypair } from "@solana/web3.js"; -import bs58 from "bs58"; - -/** - * Get default Solana wallet path (standard location) - */ -export function getDefaultWalletPath(): string { - return join(homedir(), ".config", "solana", "id.json"); -} - -/** - * Expand ~ to home directory (Node.js doesn't do this automatically) - */ -export function expandPath(path: string): string { - if (path.startsWith("~/")) { - return join(homedir(), path.slice(2)); - } - if (path === "~") { - return homedir(); - } - if (path.startsWith("~")) { - // Handle ~username format (though we'll just expand to current user's home) - return join(homedir(), path.slice(1)); - } - return path; -} - -/** - * Load wallet from file path (handles both array and base58 formats) - */ -export function loadWallet(walletPath: string): Keypair { - const expandedPath = expandPath(walletPath); - - if (!existsSync(expandedPath)) { - throw new Error(`Wallet file not found: ${expandedPath}`); - } - - const walletData = JSON.parse(readFileSync(expandedPath, "utf-8")); - - if (Array.isArray(walletData)) { - return Keypair.fromSecretKey(Uint8Array.from(walletData)); - } else if (typeof walletData === "string") { - return Keypair.fromSecretKey(bs58.decode(walletData)); - } else { - throw new Error("Invalid wallet format"); - } -} - - diff --git a/examples/integration-test-command-formats/package.json b/examples/integration-test-command-formats/package.json deleted file mode 100644 index 1e08534..0000000 --- a/examples/integration-test-command-formats/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "integration-test-command-formats", - "version": "1.0.0", - "private": true, - "description": "Integration tests for command format parsing (CLI vs JSON)", - "type": "module", - "scripts": { - "test:1": "tsx test-1-single-arg.ts", - "test:2": "tsx test-2-single-arg-quoted.ts", - "test:3": "tsx test-3-cli-flags.ts", - "test:4": "tsx test-4-json-format.ts", - "test:5": "tsx test-5-mixed-format.ts", - "test:all": "pnpm test:1 && pnpm test:2 && pnpm test:3 && pnpm test:4 && pnpm test:5" - }, - "dependencies": { - "@solana/web3.js": "^1.98.4", - "@memeputer/sdk": "^1.8.0", - "bs58": "^6.0.0", - "dotenv": "^16.4.5" - }, - "devDependencies": { - "@types/node": "^20.11.30", - "tsx": "^4.16.0", - "typescript": "^5.6.3" - } -} - diff --git a/examples/integration-test-command-formats/test-1-single-arg.ts b/examples/integration-test-command-formats/test-1-single-arg.ts deleted file mode 100644 index 158c9fe..0000000 --- a/examples/integration-test-command-formats/test-1-single-arg.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Test 1: Single Argument (No Quotes) - * - * Tests: /enhance_prompt a cyberpunk samurai - * - * This tests if the backend can parse a simple positional argument - * without quotes as the basePrompt. - */ - -import { Memeputer } from "@memeputer/sdk"; -import { loadConfig } from "./lib/config"; -import { showPaymentDetails } from "./lib/payment"; -import { loadWallet } from "./lib/wallet"; -import { checkBalance } from "./lib/balance"; -import { Connection } from "@solana/web3.js"; - -const config = loadConfig(); - -async function main() { - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - console.log("Test 1: Single Argument (No Quotes)"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); - - const wallet = loadWallet(config.walletPath); - const connection = new Connection(config.rpcUrl, "confirmed"); - await checkBalance(wallet, connection); - - const memeputer = new Memeputer({ - apiUrl: config.apiUrl, - chain: config.chain, - wallet, - connection, - verbose: true, - }); - - // Test: Send CLI format string directly via prompt() - const cliCommand = '/enhance_prompt a cyberpunk samurai'; - console.log(`πŸ“ Sending CLI format:`); - console.log(` ${cliCommand}\n`); - - try { - const result = await memeputer.prompt('promptputer', cliCommand); - - console.log("\nβœ… Response received:"); - console.log(` ${result.response.substring(0, 200)}${result.response.length > 200 ? '...' : ''}\n`); - - // Try to parse as JSON - try { - const parsed = JSON.parse(result.response); - console.log("βœ… Valid JSON response:"); - console.log(JSON.stringify(parsed, null, 2)); - if (parsed.enhancedPrompt) { - console.log(`\nβœ… Enhanced Prompt: ${parsed.enhancedPrompt.substring(0, 150)}...`); - } - } catch { - console.log("⚠️ Response is not JSON (may be plain text)"); - } - - showPaymentDetails(result); - } catch (error) { - console.error("❌ Error:", error instanceof Error ? error.message : error); - process.exit(1); - } -} - -main().catch((error) => { - console.error("❌ Error:", error.message); - process.exit(1); -}); - diff --git a/examples/integration-test-command-formats/test-2-single-arg-quoted.ts b/examples/integration-test-command-formats/test-2-single-arg-quoted.ts deleted file mode 100644 index efabb0e..0000000 --- a/examples/integration-test-command-formats/test-2-single-arg-quoted.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Test 2: Single Argument (With Quotes) - * - * Tests: /enhance_prompt "a futuristic cityscape at sunset" - * - * This tests if the backend can parse a quoted positional argument - * and properly strip the quotes. - */ - -import { Memeputer } from "@memeputer/sdk"; -import { loadConfig } from "./lib/config"; -import { showPaymentDetails } from "./lib/payment"; -import { loadWallet } from "./lib/wallet"; -import { checkBalance } from "./lib/balance"; -import { Connection } from "@solana/web3.js"; - -const config = loadConfig(); - -async function main() { - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - console.log("Test 2: Single Argument (With Quotes)"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); - - const wallet = loadWallet(config.walletPath); - const connection = new Connection(config.rpcUrl, "confirmed"); - await checkBalance(wallet, connection); - - const memeputer = new Memeputer({ - apiUrl: config.apiUrl, - chain: config.chain, - wallet, - connection, - verbose: true, - }); - - // Test: Send CLI format string with quotes - const cliCommand = '/enhance_prompt "a futuristic cityscape at sunset"'; - console.log(`πŸ“ Sending CLI format:`); - console.log(` ${cliCommand}\n`); - - try { - const result = await memeputer.prompt('promptputer', cliCommand); - - console.log("\nβœ… Response received:"); - console.log(` ${result.response.substring(0, 200)}${result.response.length > 200 ? '...' : ''}\n`); - - // Try to parse as JSON - try { - const parsed = JSON.parse(result.response); - console.log("βœ… Valid JSON response:"); - console.log(JSON.stringify(parsed, null, 2)); - if (parsed.enhancedPrompt) { - console.log(`\nβœ… Enhanced Prompt: ${parsed.enhancedPrompt.substring(0, 150)}...`); - } - } catch { - console.log("⚠️ Response is not JSON (may be plain text)"); - } - - showPaymentDetails(result); - } catch (error) { - console.error("❌ Error:", error instanceof Error ? error.message : error); - process.exit(1); - } -} - -main().catch((error) => { - console.error("❌ Error:", error.message); - process.exit(1); -}); - diff --git a/examples/integration-test-command-formats/test-3-cli-flags.ts b/examples/integration-test-command-formats/test-3-cli-flags.ts deleted file mode 100644 index 35f3399..0000000 --- a/examples/integration-test-command-formats/test-3-cli-flags.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Test 3: Multiple Arguments (CLI Flags) - * - * Tests: /enhance_prompt --basePrompt="a cat wearing sunglasses" --style="artistic" --detailLevel="high" - * - * This tests if the backend can parse CLI flags in --flag=value format. - */ - -import { Memeputer } from "@memeputer/sdk"; -import { loadConfig } from "./lib/config"; -import { showPaymentDetails } from "./lib/payment"; -import { loadWallet } from "./lib/wallet"; -import { checkBalance } from "./lib/balance"; -import { Connection } from "@solana/web3.js"; - -const config = loadConfig(); - -async function main() { - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - console.log("Test 3: Multiple Arguments (CLI Flags)"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); - - const wallet = loadWallet(config.walletPath); - const connection = new Connection(config.rpcUrl, "confirmed"); - await checkBalance(wallet, connection); - - const memeputer = new Memeputer({ - apiUrl: config.apiUrl, - chain: config.chain, - wallet, - connection, - verbose: true, - }); - - // Test: Send CLI format with flags - const cliCommand = '/enhance_prompt --basePrompt="a cat wearing sunglasses" --style="artistic" --detailLevel="high"'; - console.log(`πŸ“ Sending CLI format:`); - console.log(` ${cliCommand}\n`); - - try { - const result = await memeputer.prompt('promptputer', cliCommand); - - console.log("\nβœ… Response received:"); - console.log(` ${result.response.substring(0, 200)}${result.response.length > 200 ? '...' : ''}\n`); - - // Try to parse as JSON - try { - const parsed = JSON.parse(result.response); - console.log("βœ… Valid JSON response:"); - console.log(JSON.stringify(parsed, null, 2)); - if (parsed.enhancedPrompt) { - console.log(`\nβœ… Enhanced Prompt: ${parsed.enhancedPrompt.substring(0, 150)}...`); - if (parsed.style) { - console.log(` Style: ${parsed.style}`); - } - if (parsed.detailLevel) { - console.log(` Detail Level: ${parsed.detailLevel}`); - } - } - } catch { - console.log("⚠️ Response is not JSON (may be plain text)"); - } - - showPaymentDetails(result); - } catch (error) { - console.error("❌ Error:", error instanceof Error ? error.message : error); - process.exit(1); - } -} - -main().catch((error) => { - console.error("❌ Error:", error.message); - process.exit(1); -}); - diff --git a/examples/integration-test-command-formats/test-4-json-format.ts b/examples/integration-test-command-formats/test-4-json-format.ts deleted file mode 100644 index 766f0c5..0000000 --- a/examples/integration-test-command-formats/test-4-json-format.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Test 4: JSON Format (SDK Method) - * - * Tests: Using SDK's command() method with structured parameters - * - * This tests the standard JSON format that the SDK sends. - */ - -import { Memeputer } from "@memeputer/sdk"; -import { loadConfig } from "./lib/config"; -import { showPaymentDetails } from "./lib/payment"; -import { loadWallet } from "./lib/wallet"; -import { checkBalance } from "./lib/balance"; -import { Connection } from "@solana/web3.js"; - -const config = loadConfig(); - -async function main() { - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - console.log("Test 4: JSON Format (SDK Method)"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); - - const wallet = loadWallet(config.walletPath); - const connection = new Connection(config.rpcUrl, "confirmed"); - await checkBalance(wallet, connection); - - const memeputer = new Memeputer({ - apiUrl: config.apiUrl, - chain: config.chain, - wallet, - connection, - verbose: true, - }); - - // Test: Use SDK's command() method (sends JSON format) - console.log(`πŸ“ Using SDK command() method:`); - console.log(` memeputer.command('promptputer', 'enhance_prompt', {`); - console.log(` basePrompt: 'a space station',`); - console.log(` qualityModifiers: ['8K', 'cinematic', 'artstation'],`); - console.log(` style: 'photorealistic'`); - console.log(` })\n`); - - try { - const result = await memeputer.command('promptputer', 'enhance_prompt', { - basePrompt: 'a space station', - qualityModifiers: ['8K', 'cinematic', 'artstation'], - style: 'photorealistic', - detailLevel: 'high', - includeTechnicalSpecs: true, - }); - - console.log("\nβœ… Response received:"); - console.log(` ${result.response.substring(0, 200)}${result.response.length > 200 ? '...' : ''}\n`); - - // Try to parse as JSON - try { - const parsed = JSON.parse(result.response); - console.log("βœ… Valid JSON response:"); - console.log(JSON.stringify(parsed, null, 2)); - if (parsed.enhancedPrompt) { - console.log(`\nβœ… Enhanced Prompt: ${parsed.enhancedPrompt.substring(0, 150)}...`); - if (parsed.modifiersApplied) { - console.log(` Modifiers Applied: ${parsed.modifiersApplied.join(', ')}`); - } - } - } catch { - console.log("⚠️ Response is not JSON (may be plain text)"); - } - - showPaymentDetails(result); - } catch (error) { - console.error("❌ Error:", error instanceof Error ? error.message : error); - process.exit(1); - } -} - -main().catch((error) => { - console.error("❌ Error:", error.message); - process.exit(1); -}); - diff --git a/examples/integration-test-command-formats/test-5-mixed-format.ts b/examples/integration-test-command-formats/test-5-mixed-format.ts deleted file mode 100644 index 84f4ded..0000000 --- a/examples/integration-test-command-formats/test-5-mixed-format.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Test 5: Mixed Format (Positional + Flags) - * - * Tests: /enhance_prompt a peaceful landscape --style="minimalist" --tone="serene" - * - * This tests if the backend can handle mixed format with positional argument - * followed by CLI flags. - */ - -import { Memeputer } from "@memeputer/sdk"; -import { loadConfig } from "./lib/config"; -import { showPaymentDetails } from "./lib/payment"; -import { loadWallet } from "./lib/wallet"; -import { checkBalance } from "./lib/balance"; -import { Connection } from "@solana/web3.js"; - -const config = loadConfig(); - -async function main() { - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - console.log("Test 5: Mixed Format (Positional + Flags)"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); - - const wallet = loadWallet(config.walletPath); - const connection = new Connection(config.rpcUrl, "confirmed"); - await checkBalance(wallet, connection); - - const memeputer = new Memeputer({ - apiUrl: config.apiUrl, - chain: config.chain, - wallet, - connection, - verbose: true, - }); - - // Test: Send mixed format (positional + flags) - const cliCommand = '/enhance_prompt a peaceful landscape --style="minimalist" --tone="serene"'; - console.log(`πŸ“ Sending CLI format:`); - console.log(` ${cliCommand}\n`); - - try { - const result = await memeputer.prompt('promptputer', cliCommand); - - console.log("\nβœ… Response received:"); - console.log(` ${result.response.substring(0, 200)}${result.response.length > 200 ? '...' : ''}\n`); - - // Try to parse as JSON - try { - const parsed = JSON.parse(result.response); - console.log("βœ… Valid JSON response:"); - console.log(JSON.stringify(parsed, null, 2)); - if (parsed.enhancedPrompt) { - console.log(`\nβœ… Enhanced Prompt: ${parsed.enhancedPrompt.substring(0, 150)}...`); - if (parsed.style) { - console.log(` Style: ${parsed.style}`); - } - if (parsed.tone) { - console.log(` Tone: ${parsed.tone}`); - } - } - } catch { - console.log("⚠️ Response is not JSON (may be plain text)"); - } - - showPaymentDetails(result); - } catch (error) { - console.error("❌ Error:", error instanceof Error ? error.message : error); - process.exit(1); - } -} - -main().catch((error) => { - console.error("❌ Error:", error.message); - process.exit(1); -}); - diff --git a/examples/integration-test-command-formats/tsconfig.json b/examples/integration-test-command-formats/tsconfig.json deleted file mode 100644 index c32ee27..0000000 --- a/examples/integration-test-command-formats/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "lib": ["ES2022"], - "moduleResolution": "bundler", - "esModuleInterop": true, - "skipLibCheck": true, - "strict": true, - "resolveJsonModule": true, - "types": ["node"] - }, - "include": ["*.ts"], - "exclude": ["node_modules"] -} - diff --git a/examples/integration-test/README.md b/examples/integration-test/README.md deleted file mode 100644 index 058e7e8..0000000 --- a/examples/integration-test/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# Integration Test - -Integration testing suite for agent commands. Test all agents and their commands to verify end-to-end functionality. - -## Setup - -```bash -cd examples/integration-test -pnpm install -``` - -## Usage - -### Test All Agents and Commands -```bash -# Test everything -pnpm test:all -``` - -### Test All Commands for an Agent -```bash -# Test all keywordputer commands -pnpm test:keywordputer - -# Test all trendputer commands -pnpm test:trendputer - -# Test all promptputer commands -pnpm test:promptputer - -# Test all memeputer commands -pnpm test:memeputer -``` - -### Test Specific Command -```bash -# Test specific command with default params -pnpm test:keywordputer:keywords -pnpm test:trendputer:select_best_trend -pnpm test:promptputer:enhance_prompt -pnpm test:memeputer:ping -``` - -### Test with Custom Parameters -```bash -# Generic format: pnpm test [paramsJson] -pnpm test keywordputer keywords '{"text":"test task","maxKeywords":5}' -pnpm test trendputer select_best_trend '{"trendTitles":["NFL","Crypto"],"task":"test"}' -pnpm test promptputer enhance_prompt '{"basePrompt":"a cat"}' -``` - -## Available Agents and Commands - -- **keywordputer** - - `keywords` - Extract keywords from text - -- **trendputer** - - `discover_trends` - Discover trending topics - - `select_best_trend` - Select best trend from a list - -- **promptputer** - - `enhance_prompt` - Enhance image generation prompts - -- **memeputer** - - `ping` - Simple ping command (no params) - -## Configuration - -Uses the same config system as hello-world: -- Wallet: `MEMEPUTER_WALLET` env var or `~/.config/solana/id.json` -- API URL: `MEMEPUTER_API_URL` env var or defaults to production -- Chain: `MEMEPUTER_CHAIN` env var (default: `solana`) - -## Examples - -### Test All Commands for Keywordputer -```bash -pnpm test:keywordputer -``` - -### Test Specific Command -```bash -pnpm test:trendputer:select_best_trend -``` - -### Test with Custom Parameters -```bash -pnpm test keywordputer keywords '{"text":"Create educational content about DeFi","maxKeywords":5}' -``` - -### Test Against Localhost -```bash -export MEMEPUTER_API_URL="http://localhost:3007/x402" -pnpm test:trendputer:select_best_trend -``` - -## What It Does - -This integration test suite verifies end-to-end functionality: - -1. **Wallet Setup**: Loads your wallet and checks balance -2. **SDK Initialization**: Creates Memeputer SDK instance -3. **Command Execution**: Calls the specified agent command with provided params (or defaults) -4. **Payment Flow**: Handles x402 payment flow automatically -5. **Response Validation**: Displays the response (pretty-printed if JSON) -6. **Payment Details**: Shows transaction details and explorer links - -Perfect for: -- **Development**: Quickly test new agents or commands during development -- **CI/CD**: Run integration tests before deployment -- **Verification**: Ensure agents work correctly after backend changes - -## Adding New Test Cases - -Edit `test.ts` and add to `AGENT_TEST_CASES`: - -```typescript -const AGENT_TEST_CASES: Record> = { - // ... existing agents ... - newagent: { - newcommand: { - param1: "value1", - param2: "value2", - }, - }, -}; -``` - -Then add npm scripts in `package.json`: -```json -{ - "scripts": { - "test:newagent": "tsx test.ts newagent", - "test:newagent:newcommand": "tsx test.ts newagent newcommand" - } -} -``` diff --git a/examples/integration-test/lib/balance.ts b/examples/integration-test/lib/balance.ts deleted file mode 100644 index f8ea3d5..0000000 --- a/examples/integration-test/lib/balance.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Connection, Keypair } from "@solana/web3.js"; -import { getUsdcBalance } from "@memeputer/sdk"; - -/** - * Check wallet balance and exit if insufficient - */ -export async function checkBalance( - wallet: Keypair, - connection: Connection -): Promise { - console.log("πŸ’° Checking wallet balance..."); - - const balance = await getUsdcBalance(connection, wallet); - console.log(` Current balance: ${balance.toFixed(4)} USDC`); - console.log(` Wallet: ${wallet.publicKey.toBase58()}\n`); - - if (balance === 0) { - console.error("❌ Error: Insufficient USDC balance"); - console.error(` Your wallet has 0 USDC. Please fund your wallet with USDC to continue.`); - console.error(` Wallet address: ${wallet.publicKey.toBase58()}`); - process.exit(1); - } - - if (balance < 0.01) { - console.warn(`⚠️ Warning: Low balance (${balance.toFixed(4)} USDC)`); - console.warn(` Typical agent interactions cost 0.01-0.10 USDC`); - console.warn(` You may not have enough for this interaction.\n`); - } -} - diff --git a/examples/integration-test/lib/config.ts b/examples/integration-test/lib/config.ts deleted file mode 100644 index 25053ef..0000000 --- a/examples/integration-test/lib/config.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { config } from "dotenv"; -import { getDefaultWalletPath } from "./wallet"; - -// Load environment variables from .env file -config(); - -export interface Config { - apiUrl: string; - rpcUrl: string; - walletPath: string; - agentId: string; - message: string; - chain: string; // Blockchain: 'solana' (default) | 'base' -} - -/** - * Load configuration from environment variables with sensible defaults - */ -export function loadConfig(): Config { - return { - apiUrl: process.env.MEMEPUTER_API_URL || "https://agents.memeputer.com/x402", - rpcUrl: process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com", - walletPath: process.env.MEMEPUTER_WALLET || getDefaultWalletPath(), - agentId: process.env.MEMEPUTER_AGENT_ID || "memeputer", - message: process.env.MEMEPUTER_MESSAGE || "Hello", - chain: process.env.MEMEPUTER_CHAIN || "solana", // Default to Solana - }; -} - diff --git a/examples/integration-test/lib/payment.ts b/examples/integration-test/lib/payment.ts deleted file mode 100644 index 6d6e572..0000000 --- a/examples/integration-test/lib/payment.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { InteractionResult } from "@memeputer/sdk"; - -/** - * Detect if a transaction signature is Base/EVM (hex starting with 0x) or Solana (base58) - */ -function isBaseTransaction(signature: string): boolean { - if (!signature) return false; - // Base transaction hashes are hex strings starting with 0x and exactly 66 chars (0x + 64 hex chars) - return signature.startsWith('0x') && - signature.length === 66 && - /^0x[a-fA-F0-9]{64}$/.test(signature); -} - -/** - * Get transaction URL - automatically detects Base vs Solana - */ -function getTxUrl(signature: string): string { - if (isBaseTransaction(signature)) { - return `https://basescan.org/tx/${signature}`; - } - return `https://solscan.io/tx/${signature}`; -} - -/** - * Get account URL - automatically detects Base (0x...) vs Solana (base58) - */ -function getAccountUrl(address: string): string { - if (address.startsWith('0x') && address.length === 42) { - // Base/EVM address - return `https://basescan.org/address/${address}`; - } - // Solana address - return `https://solscan.io/account/${address}`; -} - -/** - * Display payment details in a clean format - */ -export function showPaymentDetails(result: InteractionResult) { - if (!result.transactionSignature) { - return; - } - - const txUrl = getTxUrl(result.transactionSignature); - console.log(`\nπŸ’Έ Payment Details:\n`); - console.log(`Transaction: ${txUrl}\n`); - - if (result.x402Receipt) { - const receipt = result.x402Receipt; - console.log(`Amount: ${receipt.amountPaidUsdc.toFixed(4)} USDC\n`); - - if (receipt.payer && receipt.payer !== 'unknown') { - const payerUrl = getAccountUrl(receipt.payer); - console.log(`From: ${receipt.payer}`); - console.log(`${payerUrl}\n`); - } - - if (receipt.merchant || receipt.payTo) { - const merchant = receipt.merchant || receipt.payTo; - const merchantUrl = getAccountUrl(merchant); - console.log(`To: ${merchant}`); - console.log(`${merchantUrl}\n`); - } - } else if (result.x402Quote) { - console.log(`Amount: ${result.x402Quote.amountQuotedUsdc.toFixed(4)} USDC\n`); - } -} - diff --git a/examples/integration-test/lib/wallet.ts b/examples/integration-test/lib/wallet.ts deleted file mode 100644 index 72b235c..0000000 --- a/examples/integration-test/lib/wallet.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { readFileSync, existsSync } from "fs"; -import { join } from "path"; -import { homedir } from "os"; -import { Keypair } from "@solana/web3.js"; -import bs58 from "bs58"; - -/** - * Get default Solana wallet path (standard location) - */ -export function getDefaultWalletPath(): string { - return join(homedir(), ".config", "solana", "id.json"); -} - -/** - * Expand ~ to home directory (Node.js doesn't do this automatically) - */ -export function expandPath(path: string): string { - if (path.startsWith("~/")) { - return join(homedir(), path.slice(2)); - } - if (path === "~") { - return homedir(); - } - if (path.startsWith("~")) { - // Handle ~username format (though we'll just expand to current user's home) - return join(homedir(), path.slice(1)); - } - return path; -} - -/** - * Load wallet from file path (handles both array and base58 formats) - */ -export function loadWallet(walletPath: string): Keypair { - const expandedPath = expandPath(walletPath); - - if (!existsSync(expandedPath)) { - throw new Error(`Wallet file not found: ${expandedPath}`); - } - - const walletData = JSON.parse(readFileSync(expandedPath, "utf-8")); - - if (Array.isArray(walletData)) { - return Keypair.fromSecretKey(Uint8Array.from(walletData)); - } else if (typeof walletData === "string") { - return Keypair.fromSecretKey(bs58.decode(walletData)); - } else { - throw new Error("Invalid wallet format"); - } -} - - diff --git a/examples/integration-test/package.json b/examples/integration-test/package.json deleted file mode 100644 index b25e5f0..0000000 --- a/examples/integration-test/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "integration-test", - "version": "1.0.0", - "private": true, - "description": "Integration testing suite for agent commands", - "type": "module", - "scripts": { - "test": "tsx test.ts", - "test:all": "tsx test-all.ts", - "test:keywordputer": "tsx test.ts keywordputer", - "test:keywordputer:keywords": "tsx test.ts keywordputer keywords", - "test:trendputer": "tsx test.ts trendputer", - "test:trendputer:discover_trends": "tsx test.ts trendputer discover_trends", - "test:trendputer:select_best_trend": "tsx test.ts trendputer select_best_trend", - "test:promptputer": "tsx test.ts promptputer", - "test:promptputer:enhance_prompt": "tsx test.ts promptputer enhance_prompt", - "test:imagedescripterputer": "tsx test.ts imagedescripterputer", - "test:imagedescripterputer:describe_image": "tsx test.ts imagedescripterputer describe_image", - "test:memeputer": "tsx test.ts memeputer", - "test:memeputer:ping": "tsx test.ts memeputer ping" - }, - "dependencies": { - "@solana/web3.js": "^1.98.4", - "@memeputer/sdk": "^1.8.0", - "bs58": "^6.0.0", - "dotenv": "^16.4.5" - }, - "devDependencies": { - "@types/node": "^20.11.30", - "tsx": "^4.16.0", - "typescript": "^5.6.3" - } -} - diff --git a/examples/integration-test/test-all.ts b/examples/integration-test/test-all.ts deleted file mode 100644 index b63bafe..0000000 --- a/examples/integration-test/test-all.ts +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Integration Test Suite - Run All Tests - * - * Runs integration tests for all agents and their commands. - * Verifies end-to-end functionality including payment flow. - * - * Usage: - * pnpm test:all - */ - -import { execSync } from "child_process"; -import { fileURLToPath } from "url"; -import { dirname } from "path"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Define test cases (same as test.ts) -const AGENT_TEST_CASES: Record> = { - keywordputer: { - keywords: { - text: "Find relevant topics and create a meme about them", - context: "Creating content for Solana community", - targetAudience: "Solana degens", - contentGoal: "meme", - maxKeywords: 10, - }, - }, - trendputer: { - discover_trends: { - keywords: ["crypto", "solana"], - context: "Find relevant topics and create a meme about them", - maxResults: 5, - includeHashtags: true, - includeUrl: true, - }, - select_best_trend: { - trends: ["NFL", "Crypto", "AI"], - context: "Find relevant topics and create a meme about them", - includeReasoning: true, - }, - }, - promptputer: { - enhance_prompt: { - basePrompt: "a cyberpunk samurai", - qualityModifiers: ["8K", "cinematic", "artstation"], - style: "artistic", - detailLevel: "high", - }, - }, - imagedescripterputer: { - describe_image: { - imageUrl: "https://memeputer.com/logo.png", - detailLevel: "detailed", - }, - }, - memeputer: { - ping: undefined, - }, -}; - -async function main() { - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - console.log("Testing All Agents and Commands"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); - - const agents = Object.keys(AGENT_TEST_CASES); - let passed = 0; - let failed = 0; - const failures: Array<{ agent: string; command: string; error: string }> = []; - - for (const agentId of agents) { - const commands = Object.keys(AGENT_TEST_CASES[agentId]); - console.log(`\nπŸ“‹ Testing ${agentId} (${commands.length} command${commands.length > 1 ? 's' : ''})`); - - for (const command of commands) { - try { - console.log(`\n β†’ ${agentId}.${command}`); - const params = AGENT_TEST_CASES[agentId][command]; - const paramsArg = params !== undefined ? `'${JSON.stringify(params)}'` : ''; - execSync(`tsx test.ts ${agentId} ${command} ${paramsArg}`, { - stdio: 'inherit', - cwd: __dirname, - shell: '/bin/zsh', // Use zsh to handle quotes properly - }); - passed++; - console.log(` βœ… Passed`); - } catch (error) { - failed++; - const errorMsg = error instanceof Error ? error.message : String(error); - failures.push({ agent: agentId, command, error: errorMsg }); - console.log(` ❌ Failed: ${errorMsg}`); - } - } - } - - console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - console.log("Test Summary"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - console.log(`βœ… Passed: ${passed}`); - console.log(`❌ Failed: ${failed}`); - - if (failures.length > 0) { - console.log("\nFailures:"); - failures.forEach(f => { - console.log(` ${f.agent}.${f.command}: ${f.error}`); - }); - } - - process.exit(failed > 0 ? 1 : 0); -} - -main().catch((error) => { - console.error("❌ Error:", error.message); - process.exit(1); -}); - diff --git a/examples/integration-test/test.ts b/examples/integration-test/test.ts deleted file mode 100644 index f6f266c..0000000 --- a/examples/integration-test/test.ts +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Integration Test Script for Agent Commands - * - * Tests agent commands end-to-end, including payment flow and response parsing. - * - * Usage: - * pnpm test [command] [paramsJson] - * - * Examples: - * pnpm test keywordputer # Test all keywordputer commands - * pnpm test keywordputer keywords # Test specific command - * pnpm test keywordputer keywords '{"text":"test"}' # Test with params - * pnpm test trendputer select_best_trend '{"trendTitles":["NFL","Crypto"],"task":"test"}' - * pnpm test memeputer ping # Test command without params - */ - -import { Memeputer } from "@memeputer/sdk"; -import { loadConfig } from "./lib/config"; -import { showPaymentDetails } from "./lib/payment"; -import { loadWallet } from "./lib/wallet"; -import { checkBalance } from "./lib/balance"; -import { Connection } from "@solana/web3.js"; - -const config = loadConfig(); - -// Define test cases for each agent -const AGENT_TEST_CASES: Record> = { - keywordputer: { - keywords: { - text: "Find relevant topics and create a meme about them", - context: "Creating content for Solana community", - targetAudience: "Solana degens", - contentGoal: "meme", - maxKeywords: 10, - }, - }, - trendputer: { - discover_trends: { - keywords: ["crypto", "solana"], - context: "Find relevant topics and create a meme about them", - maxResults: 5, - includeHashtags: true, - includeUrl: true, - }, - select_best_trend: { - trends: ["NFL", "Crypto", "AI"], - context: "Find relevant topics and create a meme about them", - includeReasoning: true, - }, - }, - promptputer: { - enhance_prompt: { - basePrompt: "a cyberpunk samurai", - }, - }, - imagedescripterputer: { - describe_image: { - imageUrl: "https://memeputer.com/logo.png", - detailLevel: "detailed", - }, - }, - memeputer: { - ping: undefined, // No params - }, -}; - -async function main() { - const agentId = process.argv[2]; - const command = process.argv[3]; - const paramsJson = process.argv[4]; - - if (!agentId) { - console.error("Usage: pnpm test [command] [paramsJson]"); - console.error(""); - console.error("Examples:"); - console.error(" pnpm test keywordputer # Test base endpoint (chat) + all commands"); - console.error(" pnpm test keywordputer keywords # Test specific command only"); - console.error(" pnpm test keywordputer keywords '{\"text\":\"test\"}' # Test with custom params"); - console.error(" pnpm test memeputer ping # Test command without params"); - console.error(""); - console.error("Available agents:"); - Object.keys(AGENT_TEST_CASES).forEach(agent => { - const commands = Object.keys(AGENT_TEST_CASES[agent]); - console.error(` ${agent}: ${commands.join(", ")}`); - }); - process.exit(1); - } - - const wallet = loadWallet(config.walletPath); - const connection = new Connection(config.rpcUrl, "confirmed"); - await checkBalance(wallet, connection); - - const memeputer = new Memeputer({ - apiUrl: config.apiUrl, - chain: config.chain, - wallet, - connection, - verbose: true, - }); - - // Get test cases for this agent - const agentTests = AGENT_TEST_CASES[agentId]; - if (!agentTests) { - console.error(`❌ Unknown agent: ${agentId}`); - console.error(`Available agents: ${Object.keys(AGENT_TEST_CASES).join(", ")}`); - process.exit(1); - } - - // If command specified, test only that command - if (command) { - await testCommand(memeputer, agentId, command, paramsJson, agentTests[command]); - } else { - // Test all commands for this agent + base endpoint (chat) - console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`); - console.log(`Testing all commands + chat for: ${agentId}`); - console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`); - - // First, test base endpoint (chat) - console.log(`\n[0] Testing: ${agentId} (base endpoint - chat)`); - console.log("─".repeat(60)); - await testChat(memeputer, agentId); - console.log(""); - - // Then test all commands - const commands = Object.keys(agentTests); - for (let i = 0; i < commands.length; i++) { - const cmd = commands[i]; - console.log(`\n[${i + 1}/${commands.length}] Testing: ${agentId}.${cmd}`); - console.log("─".repeat(60)); - - await testCommand(memeputer, agentId, cmd, undefined, agentTests[cmd]); - - if (i < commands.length - 1) { - console.log(""); // Spacing between tests - } - } - - console.log(`\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`); - console.log(`βœ… Completed testing all commands + chat for ${agentId}`); - console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`); - } -} - -async function testChat( - memeputer: Memeputer, - agentId: string -) { - console.log(`\nπŸ’¬ Testing: ${agentId} (base endpoint - chat)\n`); - - // Use a simple chat message to test base endpoint - const chatMessages: Record = { - keywordputer: "Extract keywords from: create a meme about crypto", - trendputer: "What are the latest trending topics?", - promptputer: "Enhance this prompt: a cyberpunk samurai", - briefputer: "Create a brief for a crypto meme", - pfpputer: "Generate an image of a cat", - imagedescripterputer: "Describe this image: https://example.com/image.jpg", - captionputer: "Write captions for a crypto meme", - broadcastputer: "Post to Telegram", - memeputer: "Hello, how are you?", - }; - - const message = chatMessages[agentId] || `Hello ${agentId}, what can you do?`; - console.log(`πŸ“ Chat message: "${message}"`); - console.log(""); - - try { - const result = await memeputer.prompt(agentId, message); - - console.log("\nβœ… Response received:"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - console.log(result.response); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); - - showPaymentDetails(result); - - console.log("\nβœ… Chat test completed successfully!"); - } catch (error) { - console.error("\n❌ Error:", error instanceof Error ? error.message : error); - if (error instanceof Error && error.stack) { - console.error(error.stack); - } - throw error; - } -} - -async function testCommand( - memeputer: Memeputer, - agentId: string, - command: string, - customParamsJson: string | undefined, - defaultParams: any -) { - console.log(`\nπŸš€ Testing: ${agentId}.${command}\n`); - - // Determine params: custom JSON > default params > undefined - let params: any = defaultParams; - if (customParamsJson) { - try { - params = JSON.parse(customParamsJson); - console.log("πŸ“ Using custom parameters:"); - console.log(JSON.stringify(params, null, 2)); - } catch (error) { - console.error("❌ Failed to parse custom params JSON:", error); - console.log("πŸ“ Using default parameters instead"); - params = defaultParams; - } - } else if (defaultParams !== undefined) { - console.log("πŸ“ Using default test parameters:"); - console.log(JSON.stringify(params, null, 2)); - } else { - console.log("πŸ“ No parameters (command doesn't require any)"); - } - console.log(""); - - try { - const result = await memeputer.command(agentId, command, params); - - console.log("\nβœ… Response received:"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - - // Try to parse as JSON for pretty printing - try { - const parsed = JSON.parse(result.response); - console.log(JSON.stringify(parsed, null, 2)); - - // Special handling for different response formats - if (parsed.data) { - if (parsed.data.keywords) { - console.log(`\nβœ… Extracted ${parsed.data.keywords.length} keywords:`); - parsed.data.keywords.forEach((kw: string, idx: number) => { - console.log(` ${idx + 1}. ${kw}`); - }); - } else if (parsed.data.selectedIndex !== undefined) { - console.log(`\nβœ… Selected trend index: ${parsed.data.selectedIndex}`); - if (parsed.data.reasoning) { - console.log(`πŸ’­ Reasoning: ${parsed.data.reasoning}`); - } - } - } - } catch { - // Not JSON, print as-is - console.log(result.response); - } - - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); - - showPaymentDetails(result); - - console.log("\nβœ… Test completed successfully!"); - } catch (error) { - console.error("\n❌ Error:", error instanceof Error ? error.message : error); - if (error instanceof Error && error.stack) { - console.error(error.stack); - } - throw error; // Re-throw so test:all can continue or fail appropriately - } -} - -main().catch((error) => { - console.error("❌ Error:", error.message); - process.exit(1); -}); - diff --git a/examples/integration-test/tsconfig.json b/examples/integration-test/tsconfig.json deleted file mode 100644 index c32ee27..0000000 --- a/examples/integration-test/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "lib": ["ES2022"], - "moduleResolution": "bundler", - "esModuleInterop": true, - "skipLibCheck": true, - "strict": true, - "resolveJsonModule": true, - "types": ["node"] - }, - "include": ["*.ts"], - "exclude": ["node_modules"] -} - diff --git a/examples/marketputer/.gitignore b/examples/marketputer/.gitignore deleted file mode 100644 index fe643c9..0000000 --- a/examples/marketputer/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/ -dist/ -.env -*.log -wallet.json - diff --git a/examples/marketputer/README.md b/examples/marketputer/README.md deleted file mode 100644 index f3bb8c5..0000000 --- a/examples/marketputer/README.md +++ /dev/null @@ -1,639 +0,0 @@ -# Marketputer - -**Your autonomous marketing assistant that creates meme-ready posts in your brand's style.** - -Marketputer is an AI agent that autonomously discovers trending topics, generates creative briefs, creates images, writes captions, and posts to social mediaβ€”all in your brand's voice and style. It uses other specialized AI agents and pays them automatically via x402 micropayments on Solana, so you get professional marketing content without lifting a finger. - -## How Does It Work? - -You fund a Solana wallet with USDC, set a budget, and Marketputer follows a structured workflow coordinating multiple specialized AI agents to create your content. It pays each agent what they charge in microtransactions using x402β€”you just set the budget and let it run. - -## The Workflow - -Marketputer follows a structured workflow, coordinating multiple specialized AI agents to create your content. Each agent charges their own rate for their services, and Marketputer pays them automatically using x402 micropaymentsβ€”you just set the budget and let it run. - -**Step 1: Extract Keywords** -- Uses **[Keywordputer](https://agents.memeputer.com/discover/keywordputer)** with command `keywords` and parameters: `text`, `context`, `targetAudience`, `contentGoal`, `maxKeywords` -- Analyzes your task and identifies relevant keywords and topics - -**Step 2: Discover Trends** -- Uses **[Trendputer](https://agents.memeputer.com/discover/trendputer)** with command `discover_trends` and parameters: `keywords`, `context`, `maxResults`, `includeHashtags`, `includeUrl` -- Investigates news stories and returns trends as JSON - -**Step 3: Select Best Trend** -- Uses **[Trendputer](https://agents.memeputer.com/discover/trendputer)** with command `select_best_trend` and parameters: `trendTitles`, `trends`, `task`, `criteria`, `returnFormat`, `includeReasoning` -- Evaluates trends and selects the highest quality option based on relevance and quality - -**Step 4: Create Creative Brief** -- Uses **[Briefputer](https://agents.memeputer.com/discover/briefputer)** with command `generate_brief` and parameters: `trendItem`, `brandProfile`/`brandAgentId`, `policy` -- Generates a strategic creative brief with angle, tone, and visual style tailored to your brand's voice - -**Step 5: Enhance Image Prompt** -- Uses **[Promptputer](https://agents.memeputer.com/discover/promptputer)** with command `enhance_prompt` and parameters: `basePrompt`, `qualityModifiers`, `style`, `detailLevel`, `includeTechnicalSpecs`, `tone` -- Takes the `angle` string from the creative brief (e.g., "while markets obsess over volatility, agents optimize for composability") as the `basePrompt` -- Enhances it with quality modifiers (8K render, cinematic lighting, etc.) to create a detailed image generation prompt - -**Step 6: Generate Image** -- Uses **[PFPputer](https://agents.memeputer.com/discover/pfpputer)** with command `pfp_with_reference` (if reference images provided) or `pfp` (otherwise) -- Parameters: `reference_image_urls` (array), `prompt` (optional) for `pfp_with_reference`, or array of prompt strings for `pfp` -- Creates the meme-ready image -- **Note:** PFPputer returns an async response with `statusUrl` for polling - the image may not be ready immediately - -**Step 7: Describe Image** -- Uses **[ImageDescripterputer](https://agents.memeputer.com/discover/imagedescripterputer)** with command `describe_image` and parameters: `imageUrl`, `detailLevel: "detailed"` -- Analyzes and describes the generated image - -**Step 8: Write Captions** -- Uses **[Captionputer](https://agents.memeputer.com/discover/captionputer)** with command `generate_captions` and parameters: `imageDescription`, `imagePrompt`, `trendItem`, `brief`, `brandProfile`/`brandAgentId`, `numVariants`, `customInstructions` -- Generates multiple caption options in your brand's tone - -**Step 9: Broadcast to Telegram** -- Uses **[Broadcastputer](https://agents.memeputer.com/discover/broadcastputer)** with command `post_telegram` and parameters: `chatId`, `caption`, `imageUrl` -- Posts the final content to Telegram - -Each step happens automaticallyβ€”Marketputer coordinates the agents, handles payments, and delivers your content. - -## Using Briefputer in Your Application - -Briefputer's `generate_brief` command requires specific payload structures. Here are complete examples: - -### Command Structure - -```typescript -await memeputer.command('briefputer', 'generate_brief', { - trendItem: { /* trend object */ }, - policy: { /* policy object */ }, - brandAgentId: 'optional-uuid', // OR brandProfile (not both) - brandProfile: { /* brand profile object */ } // OR brandAgentId (not both) -}); -``` - -### Required Parameters - -#### 1. `trendItem` (object, required) -A trend object from Trendputer's `discover_trends` or `select_best_trend` command: - -```json -{ - "id": "trend-123", - "title": "Solana NFT Marketplace Hits 1M Users", - "summary": "The Solana NFT marketplace has reached a major milestone...", - "score": 8.5, - "hashtags": ["#Solana", "#NFT", "#Web3"], - "canonicalUrl": "https://example.com/news/solana-nft-milestone" -} -``` - -**Minimum required fields:** At least `title` and `summary`. Other fields are optional but recommended. - -#### 2. `policy` (object, required) -Content policy with deny terms and disclaimer requirements: - -```json -{ - "denyTerms": ["nsfw", "scam", "gambling"], - "requireDisclaimer": false -} -``` - -- `denyTerms` (array of strings): Terms/topics to avoid in generated content -- `requireDisclaimer` (boolean): Whether to require a disclaimer in the brief - -### Brand Options (Choose One) - -#### Option A: Use `brandAgentId` (recommended if you have a brand agent) - -```typescript -{ - trendItem: { /* ... */ }, - policy: { /* ... */ }, - brandAgentId: "5ca90eb4-dda0-400f-bb90-898dcf467d4c" -} -``` - -The brand agent's profile (voice, style, etc.) will be fetched automatically. - -#### Option B: Use `brandProfile` (custom brand) - -```typescript -{ - trendItem: { /* ... */ }, - policy: { /* ... */ }, - brandProfile: { - "brandName": "My Brand", - "voice": "professional, trustworthy, innovative", - "styleKeywords": ["modern", "fintech", "clean"], - "denyTerms": ["nsfw", "scam"], - "targetAudience": "Solana degens", - "personality": "fun, crypto-native" - } -} -``` - -**Required fields for `brandProfile`:** -- `brandName` (string) -- `voice` (string) OR `personality` (string) - -**Optional fields:** -- `styleKeywords` (array of strings) -- `denyTerms` (array of strings) -- `targetAudience` (string) -- `logoUrl` (string) -- `primaryColor` (string) -- `emojiPack` (array of strings) -- `disclaimer` (string) - -### Complete Example - -```typescript -import { Memeputer } from '@memeputer/sdk'; - -const memeputer = new Memeputer({ - apiUrl: 'https://api.memeputer.com', - wallet: yourWallet, - connection: yourConnection -}); - -// Example 1: Using brandAgentId -const brief1 = await memeputer.command('briefputer', 'generate_brief', { - trendItem: { - title: "Solana NFT Marketplace Hits 1M Users", - summary: "The Solana NFT marketplace has reached a major milestone with 1 million active users.", - score: 8.5, - hashtags: ["#Solana", "#NFT"], - canonicalUrl: "https://example.com/news" - }, - policy: { - denyTerms: ["nsfw", "scam"], - requireDisclaimer: false - }, - brandAgentId: "5ca90eb4-dda0-400f-bb90-898dcf467d4c" -}); - -// Example 2: Using custom brandProfile -const brief2 = await memeputer.command('briefputer', 'generate_brief', { - trendItem: { - title: "Solana NFT Marketplace Hits 1M Users", - summary: "The Solana NFT marketplace has reached a major milestone with 1 million active users." - }, - policy: { - denyTerms: ["nsfw", "scam", "gambling"], - requireDisclaimer: true - }, - brandProfile: { - brandName: "My Brand", - voice: "professional, trustworthy", - styleKeywords: ["modern", "clean"], - denyTerms: ["nsfw"], - targetAudience: "Solana developers" - } -}); - -// Parse the response -const parsed = JSON.parse(brief1.response); -const creativeBrief = parsed.data.brief; -console.log('Angle:', creativeBrief.angle); -console.log('Tone:', creativeBrief.tone); -console.log('Visual Style:', creativeBrief.visualStyle); -``` - -### Response Structure - -Briefputer returns a JSON response with this structure: - -```json -{ - "data": { - "brief": { - "angle": "Focus on the milestone achievement...", - "tone": "celebratory, professional", - "visualStyle": ["modern", "clean", "professional"], - "callToAction": "Join the Solana NFT community...", - "negativeConstraints": ["avoid financial advice", "no gambling references"] - } - } -} -``` - -## Using Promptputer in Your Application - -Promptputer's `enhance_prompt` command takes a base prompt string and enhances it with quality modifiers for image generation. - -### Command Structure - -```typescript -await memeputer.command('promptputer', 'enhance_prompt', { - basePrompt: "your base prompt string", - qualityModifiers: ["8K", "cinematic", "artstation"], - style: "artistic", - detailLevel: "high", - includeTechnicalSpecs: true, - tone: "dramatic" -}); -``` - -### Parameters - -#### 1. `basePrompt` (string, required) -The base prompt to enhance. In Marketputer's workflow, this is the `angle` field from Briefputer's response. - -**Example:** -```typescript -// After getting brief from Briefputer: -const briefResponse = await memeputer.command('briefputer', 'generate_brief', { /* ... */ }); -const parsed = JSON.parse(briefResponse.response); -const briefAngle = parsed.data.brief.angle; -// "while markets obsess over volatility, agents optimize for composability - building utility when others build anxiety" - -// Use this as basePrompt: -const enhanced = await memeputer.command('promptputer', 'enhance_prompt', { - basePrompt: briefAngle, // <-- Just the angle string, not the whole brief - // ... other params -}); -``` - -#### 2. `qualityModifiers` (array of strings, optional) -Quality enhancement terms to add to the prompt. - -**Default:** `["8K", "cinematic", "artstation", "highly detailed", "professional quality"]` - -**Example:** -```typescript -qualityModifiers: ["8K", "cinematic", "artstation", "highly detailed", "professional quality"] -``` - -#### 3. `style` (string, optional) -Visual style descriptor. - -**Default:** `"artistic"` - -**Example:** `"artistic"`, `"realistic"`, `"minimalist"`, etc. - -#### 4. `detailLevel` (string, optional) -Level of detail in the enhanced prompt. - -**Default:** `"high"` - -**Example:** `"high"`, `"medium"`, `"low"` - -#### 5. `includeTechnicalSpecs` (boolean, optional) -Whether to include technical specifications (lighting, composition, etc.). - -**Default:** `true` - -#### 6. `tone` (string, optional) -Tone/mood for the prompt. - -**Default:** `"dramatic"` - -**Example:** `"dramatic"`, `"calm"`, `"energetic"`, etc. - -### Complete Example - -```typescript -import { Memeputer } from '@memeputer/sdk'; - -const memeputer = new Memeputer({ - apiUrl: 'https://api.memeputer.com', - wallet: yourWallet, - connection: yourConnection -}); - -// Step 1: Get brief from Briefputer -const briefResult = await memeputer.command('briefputer', 'generate_brief', { - trendItem: { title: "...", summary: "..." }, - policy: { denyTerms: [], requireDisclaimer: false }, - brandAgentId: "5ca90eb4-dda0-400f-bb90-898dcf467d4c" -}); - -const brief = JSON.parse(briefResult.response); -const briefAngle = brief.data.brief.angle; -// "while markets obsess over volatility, agents optimize for composability..." - -// Step 2: Enhance the angle with Promptputer -const enhancedResult = await memeputer.command('promptputer', 'enhance_prompt', { - basePrompt: briefAngle, // <-- Just the angle string - qualityModifiers: ['8K', 'cinematic', 'artstation', 'highly detailed'], - style: 'artistic', - detailLevel: 'high', - includeTechnicalSpecs: true, - tone: 'dramatic' -}); - -// Step 3: Parse enhanced prompt -const enhanced = JSON.parse(enhancedResult.response); -const imagePrompt = enhanced.enhancedPrompt || enhanced.data?.enhancedPrompt; -// Enhanced prompt ready for image generation -``` - -### Response Structure - -Promptputer returns a JSON response with this structure: - -```json -{ - "enhancedPrompt": "8K render, cinematic lighting, artstation quality, highly detailed professional quality image of: while markets obsess over volatility, agents optimize for composability - building utility when others build anxiety. Dramatic tone, artistic style...", - "modifiersApplied": ["8K", "cinematic", "artstation", "highly detailed", "professional quality"], - "style": "artistic", - "detailLevel": "high", - "tone": "dramatic" -} -``` - -**Note:** The response may be nested under a `data` key: `response.data.enhancedPrompt` - -## Using PFPputer in Your Application - -PFPputer's `pfp` or `pfp_with_reference` command generates images asynchronously. After payment, you receive a response with a `statusUrl` to poll for completion. - -### Command Structure - -```typescript -// Option 1: Regular pfp command (no reference images) -await memeputer.command('pfpputer', 'pfp', ['your prompt here']); - -// Option 2: pfp_with_reference command (with reference images) -await memeputer.command('pfpputer', 'pfp_with_reference', { - reference_image_urls: ['https://example.com/image1.jpg', 'https://example.com/image2.jpg'], - prompt: 'your optional prompt here' -}); -``` - -### Exact Response Structure (x402 Spec) - -After payment, PFPputer returns an `InteractionResult` object following the x402 specification: - -```typescript -{ - x402Version?: number; // x402 protocol version (backend may include) - success: boolean; // true if payment succeeded - response: string; // Human-readable message (e.g., "🎨 Your PFP is being generated! This usually takes 10-15 seconds...") - format: "text" | "image" | "video" | "audio"; // Response format - statusUrl?: string; // URL to poll for async operation status (REQUIRED for async operations) - imageUrl?: string; // Direct image URL (may be available immediately or after polling) - mediaUrl?: string; // Alternative media URL field - etaSeconds?: number; // Estimated time to completion in seconds (e.g., 15) - transactionSignature: string; // Solana transaction signature or EVM transaction hash - agentId: string; // Agent ID (e.g., "pfpputer") - timestamp?: string; // ISO timestamp (backend may include) - x402Receipt: { // Payment receipt (REQUIRED after payment) - amountPaidUsdc: number; // Actual amount paid in USDC (e.g., 0.03) - amountPaidMicroUsdc: number; // Amount paid in micro-USDC (e.g., 30000) - payTo: string; // Recipient wallet address - transactionSignature: string; // Transaction signature/hash - payer: string; // Payer wallet address - merchant: string; // Merchant wallet address (usually same as payTo) - timestamp: string; // ISO timestamp of payment - }, - x402Quote?: { // Payment quote details (optional, for reference) - amountQuotedUsdc: number; // Quoted amount in USDC - amountQuotedMicroUsdc: number; // Quoted amount in micro-USDC - maxAmountRequired: number; // Maximum amount required - } -} -``` - -### Complete Example Response - -Here's an exact example of what PFPputer returns: - -```json -{ - "x402Version": 1, - "success": true, - "response": "🎨 Your PFP is being generated! This usually takes 10-15 seconds...", - "format": "text", - "statusUrl": "https://api.memeputer.com/api/public/pfp/status/802a8ada-47d5-47a1-9821-3f3a5cebd4c5", - "imageUrl": "https://auth.memeputer.com/storage/v1/object/public/generated-images/pfp/generated/802a8ada-47d5-47a1-9821-3f3a5cebd4c5.png", - "etaSeconds": 15, - "transactionSignature": "34hB3L9dN89uDrtBn9WLqNFcRob3jPvmBYzzwKGkH9MswxuQ6EJBR5m3fJiVseJCHQnaEduQssn9gsmzcDfZdH9Y", - "agentId": "pfpputer", - "timestamp": "2025-11-27T22:55:18.143Z", - "x402Receipt": { - "amountPaidUsdc": 0.03, - "amountPaidMicroUsdc": 30000, - "payTo": "7xKXtg2CZ3q5vLxKvKvKvKvKvKvKvKvKvKvKvKvKvKvKv", - "transactionSignature": "34hB3L9dN89uDrtBn9WLqNFcRob3jPvmBYzzwKGkH9MswxuQ6EJBR5m3fJiVseJCHQnaEduQssn9gsmzcDfZdH9Y", - "payer": "YourWalletAddressHere", - "merchant": "7xKXtg2CZ3q5vLxKvKvKvKvKvKvKvKvKvKvKvKvKvKvKv", - "timestamp": "2025-11-27T22:55:18.143Z" - } -} -``` - -### Handling Async Responses - -PFPputer images are generated asynchronously. You have two options: - -**Option 1: Use `imageUrl` if available immediately** -```typescript -const result = await memeputer.command('pfpputer', 'pfp', ['your prompt']); -if (result.imageUrl) { - // Image is ready! - console.log('Image URL:', result.imageUrl); -} else if (result.statusUrl) { - // Need to poll for completion - const status = await memeputer.pollStatus(result.statusUrl); - if (status.status === 'completed' && status.imageUrl) { - console.log('Image URL:', status.imageUrl); - } -} -``` - -**Option 2: Always poll `statusUrl` (recommended)** -```typescript -const result = await memeputer.command('pfpputer', 'pfp', ['your prompt']); - -if (result.statusUrl) { - const status = await memeputer.pollStatus(result.statusUrl, { - maxAttempts: 120, // 2 minutes max - intervalMs: 1000, // Check every second - onProgress: (attempt, status) => { - console.log(`Attempt ${attempt}: ${status.status}`); - } - }); - - if (status.status === 'completed' && status.imageUrl) { - console.log('βœ… Image ready:', status.imageUrl); - } else if (status.status === 'failed') { - console.error('❌ Generation failed:', status.error); - } -} -``` - -### Important Notes - -1. **`statusUrl` is required** for async operations - always check for it -2. **`imageUrl` may be present immediately** but may not be accessible until generation completes -3. **Always poll `statusUrl`** if `imageUrl` is not accessible (404) or missing -4. **`x402Receipt` is required** after payment - use `amountPaidUsdc` for accurate cost tracking -5. **`transactionSignature`** is the Solana transaction signature or EVM transaction hash -6. **`format`** is typically `"text"` for async operations, even when generating images - -## Quick Start - -### 1. Install Dependencies - -```bash -pnpm install -``` - -### 2. Configure Environment (Optional) - -The example will automatically use your default Solana CLI wallet at `~/.config/solana/id.json` if you don't set `MEMEPUTER_WALLET`. - -To use a different wallet, copy the example environment file: - -```bash -cp .env.example .env -``` - -Then edit `.env` and set `MEMEPUTER_WALLET` to your wallet file path: - -```bash -# Use default Solana CLI wallet (leave empty) -MEMEPUTER_WALLET= - -# Or specify a custom path -MEMEPUTER_WALLET=./wallet.json -# or -MEMEPUTER_WALLET=~/.config/solana/id.json -``` - -### 3. Set Up Your Wallet - -You need a Solana wallet with USDC. Choose one: - -**Option A: Use existing Phantom wallet** -```bash -# Export your Phantom wallet: -# 1. Open Phantom browser extension -# 2. Settings β†’ Export Private Key -# 3. Save as wallet.json in this directory -``` - -**Option B: Create new wallet with Solana CLI** -```bash -# Install Solana CLI (if needed) -sh -c "$(curl -sSfL https://release.solana.com/stable/install)" - -# Generate new wallet -solana-keygen new --outfile wallet.json - -# Fund with USDC (get address with: solana address -k wallet.json) -``` - -### 4. Run the Example - -```bash -pnpm start run --budget 1.0 -``` - -That's it! The orchestrator will: -- Load your wallet -- Coordinate multiple agents -- Pay each agent automatically via x402 -- Complete the full workflow end-to-end - -The orchestrator will: -1. Pay Keywordputer to extract keywords -2. Pay Trendputer to find trends -3. Pay Trendputer to select best trend -4. Pay Briefputer to generate brief -5. Pay Promptputer to enhance prompt -6. Pay PFPputer to generate image -7. Pay ImageDescripterputer to describe image -8. Pay Captionputer to generate captions -9. Pay Broadcastputer to post to Telegram - -```bash -# Use default Solana CLI wallet (leave empty) -MEMEPUTER_WALLET= - -# Or specify custom wallet path -MEMEPUTER_WALLET=./wallet.json - -# Change RPC URL -SOLANA_RPC_URL=https://api.mainnet-beta.solana.com - -# Telegram Chat ID (required for posting to Telegram) -TELEGRAM_CHAT_ID=your-telegram-chat-id -``` - -### Telegram Setup (Optional) - -To post content to Telegram, you need to: - -1. **Add Broadcastputer_bot to your Telegram group** - - Search for `@Broadcastputer_bot` on Telegram - - Add the bot to your group or channel - - Make sure the bot has permission to send messages - -2. **Get your Telegram Chat ID** - - Add `@cherrybot` to your group or channel - - Send it the command `/id` and it will give you your chat ID - - The chat ID is usually a negative number for groups (e.g., `-1001234567890`) - -3. **Set the Chat ID in your environment** - - Add `TELEGRAM_CHAT_ID=your-chat-id` to your `.env` file - - Or set it when running: `TELEGRAM_CHAT_ID=your-chat-id pnpm start run --budget 1.0` - -**Note:** If you don't set `TELEGRAM_CHAT_ID`, Marketputer will skip the Telegram posting step and complete all other steps successfully. - -### Create Content in Your Brand's Likeness - -Marketputer can create memes that match your brand's voice, style, and visual identity. You have two options: - -**Option 1: Use a Brand Agent ID from Memeputer** - -If you have a brand agent profile on Memeputer, reference it by ID: - -```bash -pnpm start run --budget 1.0 --brand brands/memeputer.json -``` - -The `memeputer.json` file contains a `brandAgentId` that references a brand profile stored on Memeputer's platform. This gives you access to pre-configured brand settings including voice, style, and visual guidelines. - -**Option 2: Create a Custom Brand Profile** - -Create your own brand JSON file (like `brands/payai.json`) to define your brand's personality: - -```json -{ - "brandName": "Your Brand", - "voice": "professional, trustworthy, innovative", - "styleKeywords": ["modern", "clean", "professional"], - "denyTerms": ["nsfw", "scam"], - "referenceImageUrls": [ - "https://example.com/brand-image-1.jpg", - "https://example.com/brand-image-2.jpg" - ], - "captionPuterOptions": { - "promptTemplate": "Keep captions professional but engaging..." - } -} -``` - -Then use it: - -```bash -pnpm start run --budget 1.0 --brand brands/your-brand.json -``` - -**Brand Profile Fields:** - -- **`brandAgentId`**: Reference a brand agent profile from Memeputer's platform (alternative to custom brand profile) -- **`brandName`**: Your brand's name -- **`voice`**: Describes your brand's communication style (e.g., "professional, trustworthy" or "fun, crypto-native") -- **`styleKeywords`**: Visual style descriptors (e.g., "modern", "fintech", "clean") -- **`referenceImageUrls`**: Image URLs that PFPputer uses as style references for image generation. If not provided, PFPputer defaults to Memeputer brand style. -- **`denyTerms`**: Terms to avoid in content generation -- **`captionPuterOptions.promptTemplate`**: Custom instructions for Captionputer to match your brand's voice - -**Note:** PFPputer defaults to Memeputer brand style unless you provide `referenceImageUrls` in your brand profile. Include reference images to ensure generated images match your brand's visual identity. - -## Learn More - -- [Build your own AI agent on Memeputer](https://memeputer.com) -- [Discover and use AI agents](https://agents.memeputer.com) -- [Learn about x402 micropayments](https://x402.dev) diff --git a/examples/marketputer/brands/README.md b/examples/marketputer/brands/README.md deleted file mode 100644 index 37a6687..0000000 --- a/examples/marketputer/brands/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Brand Configurations - -Brand configs define the voice, style, and personality for content generation. - -## Supported Formats - -### Option 1: Brand Agent ID (Recommended) -```json -{ - "brandAgentId": "5ca90eb4-dda0-400f-bb90-898dcf467d4c", - "referenceImageUrls": [] -} -``` -Uses the brand agent's configured profile from Memeputer. - -### Option 2: Brand Profile Object -```json -{ - "brandName": "Pay.ai", - "voice": "professional, trustworthy, innovative", - "personality": "professional, payment-focused", - "targetAudience": "fintech professionals", - "denyTerms": ["nsfw", "scam"], - "referenceImageUrls": ["https://..."] -} -``` - -## Available Brands - -- `memeputer.json` - Memeputer brand (uses brandAgentId) -- `payai.json` - Pay.ai brand (full profile) - -## Usage - -```bash -# Use Memeputer brand -pnpm go --brand brands/memeputer.json - -# Use Pay.ai brand -pnpm go --brand brands/payai.json - -# Use custom brand -pnpm go --brand /path/to/custom-brand.json -``` - diff --git a/examples/marketputer/brands/memeputer.json b/examples/marketputer/brands/memeputer.json deleted file mode 100644 index 44a93e4..0000000 --- a/examples/marketputer/brands/memeputer.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "brandAgentId": "5ca90eb4-dda0-400f-bb90-898dcf467d4c", - "referenceImageUrls": [], - "captionPuterOptions": { - "promptTemplate": "Channel 3am thoughts, brainrot, midscroll thoughts that you couldn't hold in. Write like bantering with a close friend at 3am. Use internet slang liberally. Mix lowercase, ALL UPPERCASE, or a combination. Use ellipsis (...) frequently. Keep it raw, unfiltered, and authentically chaotic." - } -} - diff --git a/examples/marketputer/brands/payai.json b/examples/marketputer/brands/payai.json deleted file mode 100644 index a05df45..0000000 --- a/examples/marketputer/brands/payai.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "brandName": "PAYAI", - "voice": "professional, trustworthy, innovative, payment-focused", - "styleKeywords": ["modern", "fintech", "clean", "professional"], - "allowTerms": [], - "denyTerms": ["nsfw", "scam", "gambling"], - "disclaimer": "Not financial advice. DYOR.", - "primaryColor": "#0066FF", - "emojiPack": ["πŸ’³", "⚑", "πŸ”’", "πŸš€", "πŸ’°"], - "reference_image_urls": [ - "https://auth.memeputer.com/storage/v1/object/public/pfp_uploads/cdb2eb7f-3805-43c1-94bc-ba7d4fd768db/7e63f8d1-0bff-41bc-a0cd-f169a6441a41/1761146631963.jpg", - "https://auth.memeputer.com/storage/v1/object/public/pfp_uploads/cdb2eb7f-3805-43c1-94bc-ba7d4fd768db/7e63f8d1-0bff-41bc-a0cd-f169a6441a41/1761146632642.jpg", - "https://auth.memeputer.com/storage/v1/object/public/pfp_uploads/cdb2eb7f-3805-43c1-94bc-ba7d4fd768db/7e63f8d1-0bff-41bc-a0cd-f169a6441a41/1761146633279.jpg" - ], - "captionPuterOptions": { - "promptTemplate": "Focus on payment innovation and fintech. Keep captions professional but engaging. Include relevant payment emojis. Avoid overly casual language." - } -} - diff --git a/examples/marketputer/brands/tgmetrics.json b/examples/marketputer/brands/tgmetrics.json deleted file mode 100644 index ef18a78..0000000 --- a/examples/marketputer/brands/tgmetrics.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "brandName": "tgmetrics", - "voice": "data-driven, objective, insightful, growth-focused, crypto-native", - "styleKeywords": [ - "analytics", - "telegram", - "crypto", - "clean", - "modern", - "professional" - ], - "allowTerms": [ - "Telegram", - "growth", - "metrics", - "analytics", - "rankings", - "investors", - "communities", - "launchpads", - "blips" - ], - "denyTerms": [ - "nsfw", - "scam", - "gambling", - "price prediction", - "financial advice", - "pump and dump" - ], - "disclaimer": "Not financial advice. Do your own research.", - "primaryColor": "#0EA5E9", - "emojiPack": [ - "πŸ“ˆ", - "πŸ“Š", - "πŸ”", - "πŸš€", - "πŸ†", - "πŸ€–" - ], - "reference_image_urls": [ - "https://auth.memeputer.com/storage/v1/object/public/pfp_uploads/cdb2eb7f-3805-43c1-94bc-ba7d4fd768db/84cf0799-2b1d-4d7e-ba86-3f10f52c6b7b/1761227145423.jpg", - "https://auth.memeputer.com/storage/v1/object/public/pfp_uploads/cdb2eb7f-3805-43c1-94bc-ba7d4fd768db/84cf0799-2b1d-4d7e-ba86-3f10f52c6b7b/1761227146357.png", - "https://auth.memeputer.com/storage/v1/object/public/pfp_uploads/cdb2eb7f-3805-43c1-94bc-ba7d4fd768db/84cf0799-2b1d-4d7e-ba86-3f10f52c6b7b/1761227147534.png", - "https://auth.memeputer.com/storage/v1/object/public/pfp_uploads/cdb2eb7f-3805-43c1-94bc-ba7d4fd768db/84cf0799-2b1d-4d7e-ba86-3f10f52c6b7b/1761497906825.png", - "https://auth.memeputer.com/storage/v1/object/public/pfp_uploads/cdb2eb7f-3805-43c1-94bc-ba7d4fd768db/84cf0799-2b1d-4d7e-ba86-3f10f52c6b7b/1761497906256.png" - ], - "captionPuterOptions": { - "promptTemplate": "Focus on tgmetrics and Telegram community analytics, growth insights, and investor discovery. Keep tone objective, concise, and data-driven. Include relevant analytics emojis. Avoid hype, price calls, or financial advice." - } -} \ No newline at end of file diff --git a/examples/marketputer/package.json b/examples/marketputer/package.json deleted file mode 100644 index 9958b34..0000000 --- a/examples/marketputer/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "marketputer", - "version": "0.1.0", - "private": true, - "description": "Your autonomous marketing assistant that creates meme-ready posts in your brand's style", - "license": "MIT", - "type": "commonjs", - "main": "dist/index.js", - "bin": { - "marketputer": "dist/cli.js" - }, - "scripts": { - "build": "tsc -p tsconfig.json", - "start": "pnpm exec tsx src/cli.ts", - "dev": "pnpm exec tsx watch src/cli.ts", - "lint": "eslint src --ext .ts", - "lint:fix": "eslint src --ext .ts --fix", - "test": "vitest", - "test:ui": "vitest --ui", - "test:run": "vitest run", - "memeputer": "pnpm exec tsx src/cli.ts run --budget 1.0 --brand brands/memeputer.json", - "memeputer:loop": "pnpm exec tsx src/cli.ts run --budget 1.0 --brand brands/memeputer.json --loop", - "payai": "pnpm exec tsx src/cli.ts run --budget 1.0 --brand brands/payai.json", - "tgmetrics": "pnpm exec tsx src/cli.ts run --budget 1.0 --brand brands/tgmetrics.json" - }, - "packageManager": "pnpm@8.15.0", - "dependencies": { - "@memeputer/sdk": "^1.8.0", - "@solana/web3.js": "^1.98.4", - "axios": "^1.7.7", - "bs58": "^6.0.0", - "commander": "^12.1.0", - "dotenv": "^16.4.5", - "ora": "^8.0.1", - "zod": "^4.1.12" - }, - "devDependencies": { - "@types/node": "^20.11.30", - "@vitest/ui": "^4.0.8", - "tsx": "^4.16.0", - "typescript": "^5.6.3", - "vitest": "^4.0.8" - }, - "keywords": [ - "memeputer", - "x402", - "solana", - "agents", - "marketputer", - "autonomous-agents" - ] -} diff --git a/examples/marketputer/src/__tests__/orchestrator.test.ts b/examples/marketputer/src/__tests__/orchestrator.test.ts deleted file mode 100644 index e56939a..0000000 --- a/examples/marketputer/src/__tests__/orchestrator.test.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { Connection, Keypair } from '@solana/web3.js'; -import { Orchestrator } from '../orchestrator'; -import { TaskRequest } from '../types'; - -// Mock the SDK -vi.mock('@memeputer/sdk', async () => { - const actual = await vi.importActual('@memeputer/sdk'); - class MockMemeputer { - prompt = vi.fn(); - command = vi.fn(); - pollStatus = vi.fn(); - } - return { - ...actual, - Memeputer: MockMemeputer, - getUsdcBalance: vi.fn(), - }; -}); - -// Mock the logger -vi.mock('../lib/logger', () => { - class MockCleanLogger { - section = vi.fn(); - startLoading = vi.fn(); - stopLoading = vi.fn(); - result = vi.fn(); - info = vi.fn(); - warn = vi.fn(); - error = vi.fn(); - logError = vi.fn(); - log = vi.fn(); - spacer = vi.fn(); - payment = vi.fn(); - failLoading = vi.fn(); - } - return { - CleanLogger: MockCleanLogger, - }; -}); - -// Mock utils -vi.mock('../lib/utils', () => ({ - detectNetwork: vi.fn(() => 'mainnet' as const), - getSolscanTxUrl: vi.fn((tx: string) => `https://solscan.io/tx/${tx}`), - getSolscanAccountUrl: vi.fn((addr: string) => `https://solscan.io/account/${addr}`), -})); - -describe('Orchestrator', () => { - let orchestrator: Orchestrator; - let mockWallet: Keypair; - let mockConnection: Connection; - let mockMemeputer: any; - let mockGetUsdcBalance: any; - - beforeEach(async () => { - mockWallet = Keypair.generate(); - mockConnection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed'); - - const { Memeputer, getUsdcBalance } = await import('@memeputer/sdk'); - mockGetUsdcBalance = getUsdcBalance as any; - mockGetUsdcBalance.mockResolvedValue(10.0); // Default balance - - orchestrator = new Orchestrator({ - wallet: mockWallet, - connection: mockConnection, - apiBase: 'https://agents.memeputer.com/x402', - }); - - mockMemeputer = (orchestrator as any).memeputer; - }); - - describe('constructor', () => { - it('should initialize with provided config', () => { - expect(orchestrator).toBeInstanceOf(Orchestrator); - expect((orchestrator as any).wallet).toBe(mockWallet); - expect((orchestrator as any).connection).toBe(mockConnection); - expect((orchestrator as any).apiBase).toBe('https://agents.memeputer.com/x402'); - }); - - it('should initialize tracking variables', () => { - expect((orchestrator as any).totalSpent).toBe(0); - expect((orchestrator as any).agentsHired).toEqual([]); - expect((orchestrator as any).payments).toEqual([]); - }); - }); - - describe('getBalance()', () => { - it('should return USDC balance', async () => { - mockGetUsdcBalance.mockResolvedValue(5.5); - const balance = await orchestrator.getBalance(); - expect(balance).toBe(5.5); - expect(mockGetUsdcBalance).toHaveBeenCalledWith(mockConnection, mockWallet); - }); - }); - - describe('executeTask()', () => { - const mockTaskRequest: TaskRequest = { - task: 'Find relevant topics and create a meme about them', - budgetUsdc: 1.0, - }; - - beforeEach(() => { - // Mock all the internal method calls - vi.spyOn(orchestrator as any, 'whatShouldIFocusOn').mockResolvedValue({ - keywords: ['crypto', 'memes'], - }); - vi.spyOn(orchestrator as any, 'discoverTrends').mockResolvedValue({ - items: [ - { title: 'Test Trend', summary: 'Test summary', id: '1' }, - ], - }); - vi.spyOn(orchestrator as any, 'selectBestTrend').mockResolvedValue({ - title: 'Test Trend', - summary: 'Test summary', - id: '1', - }); - vi.spyOn(orchestrator as any, 'createBrief').mockResolvedValue({ - brief: { - angle: 'Test angle', - tone: 'funny', - visualStyle: ['meme'], - callToAction: 'Check it out', - }, - }); - vi.spyOn(orchestrator as any, 'enhanceImagePrompt').mockResolvedValue('Enhanced prompt'); - vi.spyOn(orchestrator as any, 'generateImage').mockResolvedValue({ - imageUrl: 'https://example.com/image.png', - imageHash: 'hash123', - imageStatusUrl: null, - }); - vi.spyOn(orchestrator as any, 'describeImage').mockResolvedValue({ - description: 'A funny meme image', - data: { style: 'meme' }, - }); - vi.spyOn(orchestrator as any, 'writeCaptions').mockResolvedValue({ - caption: 'Funny caption', - captionData: { text: 'Funny caption', hashtags: [] }, - captionOptions: [{ text: 'Funny caption', hashtags: [] }], - }); - vi.spyOn(orchestrator as any, 'broadcastToTelegram').mockResolvedValue({ - telegram: 'https://t.me/test/123', - }); - // Mock waitForImageReady to avoid real HTTP requests - vi.spyOn(orchestrator as any, 'waitForImageReady').mockResolvedValue('https://example.com/image.png'); - }); - - it('should reset tracking variables at start', async () => { - // Set some initial state - (orchestrator as any).totalSpent = 5.0; - (orchestrator as any).agentsHired = ['agent1']; - (orchestrator as any).payments = [{ agentId: 'agent1', command: 'test', amount: 1, txId: 'tx1' }]; - - await orchestrator.executeTask(mockTaskRequest); - - expect((orchestrator as any).totalSpent).toBeGreaterThanOrEqual(0); - // Note: These will be populated during execution, but reset at start - }); - - it('should execute full workflow successfully', async () => { - const result = await orchestrator.executeTask(mockTaskRequest); - - expect(result.success).toBe(true); - expect(result.totalSpent).toBeGreaterThanOrEqual(0); - expect(result.artifacts).toBeDefined(); - expect(result.artifacts?.trends).toBeDefined(); - expect(result.artifacts?.brief).toBeDefined(); - }); - - it('should handle errors gracefully', async () => { - vi.spyOn(orchestrator as any, 'whatShouldIFocusOn').mockRejectedValue( - new Error('Network error') - ); - - const result = await orchestrator.executeTask(mockTaskRequest); - - expect(result.success).toBe(false); - expect(result.error).toBe('Network error'); - expect(result.totalSpent).toBe(0); - }); - - it('should use default brand profile when not provided', async () => { - const result = await orchestrator.executeTask(mockTaskRequest); - - expect(result.success).toBe(true); - expect(result.artifacts?.brandProfile).toBeNull(); - }); - - it('should use provided brand profile', async () => { - const requestWithBrand: TaskRequest = { - ...mockTaskRequest, - brandProfile: { - brandName: 'Test Brand', - personality: 'funny', - targetAudience: 'developers', - voice: 'casual', - denyTerms: [], - }, - }; - - const result = await orchestrator.executeTask(requestWithBrand); - - expect(result.success).toBe(true); - expect(result.artifacts?.brandProfile).toBeDefined(); - expect(result.artifacts?.brandProfile?.brandName).toBe('Test Brand'); - }); - - it('should handle missing image gracefully', async () => { - vi.spyOn(orchestrator as any, 'generateImage').mockResolvedValue({ - imageUrl: null, - imageHash: null, - imageStatusUrl: null, - }); - - const result = await orchestrator.executeTask(mockTaskRequest); - - expect(result.success).toBe(true); - expect(result.artifacts?.imageGeneration?.imageUrl).toBeUndefined(); - }); - - it('should handle missing captions gracefully', async () => { - vi.spyOn(orchestrator as any, 'writeCaptions').mockResolvedValue({ - caption: null, - captionData: null, - captionOptions: [], - }); - - const result = await orchestrator.executeTask(mockTaskRequest); - - expect(result.success).toBe(true); - expect(result.artifacts?.caption).toBeNull(); - }); - }); -}); - diff --git a/examples/marketputer/src/cli.ts b/examples/marketputer/src/cli.ts deleted file mode 100644 index c661c37..0000000 --- a/examples/marketputer/src/cli.ts +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env node -import 'dotenv/config'; -import { Command } from 'commander'; -import { createRunCommand } from './commands/run'; - -const program = new Command(); - -program - .name('marketputer') - .description('Your autonomous marketing assistant that creates meme-ready posts in your brand\'s style') - .version('0.1.0'); - -// Register commands -program.addCommand(createRunCommand()); - -program.parse(); - diff --git a/examples/marketputer/src/commands/run.ts b/examples/marketputer/src/commands/run.ts deleted file mode 100644 index af4de8f..0000000 --- a/examples/marketputer/src/commands/run.ts +++ /dev/null @@ -1,418 +0,0 @@ -import { Command } from 'commander'; -import { readFileSync, existsSync } from 'fs'; -import { join } from 'path'; -import { homedir } from 'os'; -import { Connection, Keypair } from '@solana/web3.js'; -import bs58 from 'bs58'; -import { getUsdcBalance, getBaseUsdcBalance, autoDetectBaseWallet } from '@memeputer/sdk'; -import { Orchestrator } from '../orchestrator'; -import { BrandProfile, BrandProfileSchema } from '../types'; -import { CleanLogger } from '../lib/logger'; - -export function createRunCommand(): Command { - return new Command('run') - .description('Run autonomous agent economy: Find relevant topics and create a meme about them') - .option('--task ', 'DEPRECATED: Task is now fixed to "Find relevant topics and create a meme about them"') - .requiredOption('--budget ', 'Budget in USDC for the orchestrator agent') - .option('--brand ', 'Path to brand config JSON file (supports brandAgentId or brandProfile)') - .option('--agent-id ', 'Orchestrator agent ID (default: 1e7d0044-10c6-4036-9903-6ea995be82ec)') - .option('--wallet ', 'Path to wallet JSON file (defaults to ~/.config/solana/id.json or set MEMEPUTER_WALLET in .env)') - .option('--api-base ', 'Memeputer API base URL (or set MEMEPUTER_API_BASE in .env)') - .option('--rpc-url ', 'Solana RPC URL (or set SOLANA_RPC_URL in .env)') - .option('--loop', 'Run continuously in a loop (press Ctrl+C to stop)') - .option('--loop-delay ', 'Delay between loop iterations in seconds (default: 60)', '60') - .action(async (opts) => { - const logger = new CleanLogger(); - try { - const budgetUsdc = parseFloat(opts.budget); - const apiBase = opts.apiBase || process.env.MEMEPUTER_API_BASE || process.env.MEMEPUTER_API_URL || 'https://agents.memeputer.com/x402'; - - // Fixed task: Find relevant topics and create a meme about them - const task = 'Find relevant topics and create a meme about them'; - - if (opts.task) { - logger.warn('Note: --task option is deprecated. Task is now fixed to: "Find relevant topics and create a meme about them"'); - } - - // Load wallet - defaults to ~/.config/solana/id.json (same as hello-world) - function getDefaultWalletPath(): string { - return join(homedir(), '.config', 'solana', 'id.json'); - } - - function expandPath(path: string): string { - if (path.startsWith('~/')) { - return join(homedir(), path.slice(2)); - } - if (path === '~') { - return homedir(); - } - if (path.startsWith('~')) { - return join(homedir(), path.slice(1)); - } - return path; - } - - function loadWallet(walletPath: string): Keypair { - const expandedPath = expandPath(walletPath); - - if (!existsSync(expandedPath)) { - throw new Error(`Wallet file not found: ${expandedPath}`); - } - - const walletData = JSON.parse(readFileSync(expandedPath, 'utf-8')); - - if (Array.isArray(walletData)) { - return Keypair.fromSecretKey(Uint8Array.from(walletData)); - } else if (typeof walletData === 'string') { - return Keypair.fromSecretKey(bs58.decode(walletData)); - } else { - throw new Error('Invalid wallet format'); - } - } - - // Get wallet path: flag > env var > default Solana CLI wallet - const walletPath = opts.wallet || process.env.MEMEPUTER_WALLET || getDefaultWalletPath(); - const wallet = loadWallet(walletPath); - - // Get RPC URL - const rpcUrl = opts.rpcUrl || process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com'; - - // Load brand profile - default to memeputer.json if not specified - let brandProfile: BrandProfile | undefined; - const brandPathToLoad = opts.brand || 'memeputer.json'; - - try { - const brandPath = brandPathToLoad.startsWith('~/') - ? brandPathToLoad.replace('~', homedir()) - : brandPathToLoad; - - if (!existsSync(brandPath)) { - // Try relative to brands directory - const brandsPath = join(process.cwd(), 'brands', brandPath); - if (existsSync(brandsPath)) { - const brandContent = readFileSync(brandsPath, 'utf-8'); - brandProfile = BrandProfileSchema.parse(JSON.parse(brandContent)); - } else { - // If no brand specified and memeputer.json doesn't exist, continue without brand - if (!opts.brand) { - brandProfile = undefined; - } else { - throw new Error(`Brand config not found: ${brandPath}`); - } - } - } else { - const brandContent = readFileSync(brandPath, 'utf-8'); - brandProfile = BrandProfileSchema.parse(JSON.parse(brandContent)); - } - - if (brandProfile) { - if (brandProfile.brandAgentId) { - logger.log(`🎨 Brand: Using brand agent ${brandProfile.brandAgentId}`); - } else if (brandProfile.brandName) { - logger.log(`🎨 Brand: ${brandProfile.brandName}`); - } - } - } catch (error) { - // Only show error if brand was explicitly specified - if (opts.brand) { - logger.logError(`⚠️ Failed to load brand config: ${error instanceof Error ? error.message : error}`); - logger.logError(' Continuing without brand profile...\n'); - } - brandProfile = undefined; - } - - logger.log('\nπŸ€– Agent Economy Example\n'); - logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); - logger.log(`πŸ“‹ Task: ${task}`); - logger.log(' 🎯 Orchestrator will autonomously discover trending topics'); - logger.log(' 🎯 and create a meme about them'); - logger.log(`πŸ’° Budget: ${budgetUsdc} USDC`); - if (brandProfile) { - if (brandProfile.brandAgentId) { - logger.log(`🎨 Brand Agent: ${brandProfile.brandAgentId}`); - } else { - logger.log(`🎨 Brand: ${brandProfile.brandName || 'Custom'}`); - } - } - // Show wallet addresses - check if Base wallet is available - let baseWalletAddress: string | undefined; - try { - const baseWallet = autoDetectBaseWallet(); - baseWalletAddress = baseWallet.address; - } catch { - // Base wallet not configured - } - - if (baseWalletAddress) { - logger.log(`πŸ”‘ Solana Wallet: ${wallet.publicKey.toString()}`); - logger.log(`πŸ”‘ Base Wallet: ${baseWalletAddress}`); - } else { - logger.log(`πŸ”‘ Wallet: ${wallet.publicKey.toString()}`); - } - logger.log(`🌐 API: ${apiBase}`); - logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); - - const connection = new Connection(rpcUrl, 'confirmed'); - - // Check wallet balances before proceeding - // Since agents may request Base payments, check both Solana and Base balances - logger.log('πŸ’° Checking wallet balances...'); - - // Check Solana balance - const solanaBalance = await getUsdcBalance(connection, wallet); - logger.log(` Solana USDC balance: ${solanaBalance.toFixed(4)} USDC (${wallet.publicKey.toString()})`); - - // Try to check Base balance (may not be configured) - let baseBalance = 0; - if (baseWalletAddress) { - // Base wallet was already loaded above, use it for balance check - try { - const baseWallet = autoDetectBaseWallet(); - baseBalance = await getBaseUsdcBalance(baseWallet); - logger.log(` Base USDC balance: ${baseBalance.toFixed(4)} USDC (${baseWalletAddress})`); - } catch (error) { - logger.log(` Base wallet: Error checking balance`); - } - } else { - logger.log(` Base wallet: Not configured (agents requesting Base payments will auto-load if available)`); - } - - logger.log(` Budget requested: ${budgetUsdc.toFixed(4)} USDC`); - - // Check if we have sufficient balance on either network - // Since agents may request Base payments, we need Base balance - // But we'll check Solana balance for backward compatibility - const hasSolanaBalance = solanaBalance >= budgetUsdc; - const hasBaseBalance = baseBalance >= budgetUsdc; - - if (!hasSolanaBalance && !hasBaseBalance) { - logger.logError('\n❌ ERROR: Insufficient USDC balance on both networks!'); - logger.logError(` Solana Wallet: ${wallet.publicKey.toString()}`); - logger.logError(` Solana Balance: ${solanaBalance.toFixed(4)} USDC`); - if (baseWalletAddress) { - logger.logError(` Base Wallet: ${baseWalletAddress}`); - logger.logError(` Base Balance: ${baseBalance.toFixed(4)} USDC`); - } - logger.logError(` Budget: ${budgetUsdc.toFixed(4)} USDC`); - logger.logError('\n Please add more USDC to your wallet(s) before proceeding.'); - logger.logError(' Execution stopped to prevent partial failures.\n'); - process.exit(1); - } else { - const network = hasBaseBalance ? 'Base' : 'Solana'; - const balance = hasBaseBalance ? baseBalance : solanaBalance; - const remaining = balance - budgetUsdc; - logger.log(` βœ… Sufficient balance on ${network} (${remaining.toFixed(4)} USDC remaining after budget)`); - if (hasBaseBalance && baseWalletAddress !== 'N/A') { - logger.info(`Note: Agents requesting Base payments will use Base wallet: ${baseWalletAddress}`); - } - logger.spacer(); - } - - // Create orchestrator - const orchestrator = new Orchestrator({ - wallet, - connection, - apiBase, - }); - - // Run the task - logger.log('πŸš€ Starting execution...'); - logger.log(' The orchestrator will discover trending topics'); - logger.log(' and create a meme about them.'); - logger.log('\nπŸ“‹ Execution Plan:'); - logger.log(' 1. What\'s the Plan? - Briefputer analyzes task and identifies keywords/topics'); - logger.log(' 2. Discover Trends - Trendputer investigates 10 trending topics'); - logger.log(' 3. Select Best Trend - Briefputer evaluates and selects highest quality trend'); - logger.log(' 4. Create Creative Brief - Briefputer generates strategic brief with angle, tone, and style'); - logger.log(' 5. Enhance Image Prompt - Promptputer refines prompt with quality modifiers'); - logger.log(' 6. Generate Image - PFPputer creates meme-ready image'); - logger.log(' 7. Describe Image - ImageDescripterputer analyzes and describes the image'); - logger.log(' 8. Write Captions - Captionputer generates multiple caption options'); - logger.log(' 9. Broadcast to Telegram - Broadcastputer posts final content'); - logger.log('\nπŸ’Έ Each step involves paying agents via x402 micropayments'); - logger.log(' All payments tracked with blockchain explorer links (Basescan for Base, Solscan for Solana)\n'); - - const loopEnabled = opts.loop || false; - const loopDelaySeconds = parseInt(opts.loopDelay || '60', 10); - let iteration = 0; - - // Handle graceful shutdown - let shouldStop = false; - const stopHandler = () => { - logger.log('\n\nπŸ›‘ Stopping loop... (Press Ctrl+C again to force exit)'); - shouldStop = true; - }; - process.on('SIGINT', stopHandler); - process.on('SIGTERM', stopHandler); - - do { - iteration++; - if (loopEnabled && iteration > 1) { - logger.log(`\n\n${'='.repeat(60)}`); - logger.log(`πŸ”„ Starting iteration #${iteration}`); - logger.log(`${'='.repeat(60)}\n`); - } - - const result = await orchestrator.executeTask({ - task: task, - budgetUsdc, - brandProfile, - }); - - if (result.success) { - logger.log('\nβœ… Task completed successfully!'); - logger.log('\nπŸ“Š Summary:'); - logger.log(` Total spent: ${result.totalSpent.toFixed(4)} USDC`); - logger.log(` Agents hired: ${result.agentsHired.length}`); - logger.log(` Payments made: ${result.payments.length}`); - - // Display detailed admin information - if (result.artifacts) { - // Trend information - if (result.artifacts.trends?.selectedTrend) { - const trend = result.artifacts.trends.selectedTrend; - logger.log(`\nπŸ“ˆ Selected Trend:`); - logger.log(` Title: ${trend.title || 'N/A'}`); - logger.log(` Summary: ${trend.summary || 'N/A'}`); - if (trend.hashtags && trend.hashtags.length > 0) { - logger.log(` Hashtags: ${trend.hashtags.join(', ')}`); - } - if (trend.canonicalUrl) { - logger.log(` URL: ${trend.canonicalUrl}`); - } - } - - // Brief information - if (result.artifacts.brief) { - const brief = result.artifacts.brief; - logger.log(`\nπŸ“ Creative Brief:`); - logger.log(` Angle: ${brief.angle || 'N/A'}`); - logger.log(` Tone: ${brief.tone || 'N/A'}`); - if (brief.visualStyle && brief.visualStyle.length > 0) { - logger.log(` Visual Style: ${brief.visualStyle.join(', ')}`); - } - logger.log(` CTA: ${brief.callToAction || 'N/A'}`); - if (brief.negativeConstraints && brief.negativeConstraints.length > 0) { - logger.log(` Constraints: ${brief.negativeConstraints.join(', ')}`); - } - } - - // Image generation details - if (result.artifacts.imageGeneration) { - const img = result.artifacts.imageGeneration; - logger.log(`\n🎨 Image Generation:`); - // Show trend title instead of long prompt - if (result.artifacts.trends?.selectedTrend?.title) { - logger.log(` Trend: ${result.artifacts.trends.selectedTrend.title}`); - } else if (result.artifacts.trends?.items?.[0]?.title) { - logger.log(` Trend: ${result.artifacts.trends.items[0].title}`); - } - if (img.imageUrl) { - logger.log(` Image URL: ${img.imageUrl}`); - } - if (img.imageHash) { - logger.log(` Image Hash: ${img.imageHash}`); - } - if (img.seed) { - logger.log(` Seed: ${img.seed}`); - } - if (img.guidance) { - logger.log(` Guidance: ${img.guidance}`); - } - } - - // Image description - if (result.artifacts.imageDescription) { - const desc = result.artifacts.imageDescription; - logger.log(`\nπŸ‘οΈ Image Description:`); - if (desc.description) { - const preview = desc.description.substring(0, 200); - logger.log(` ${preview}${desc.description.length > 200 ? '...' : ''}`); - } - } - - // Caption information - if (result.artifacts.caption) { - const cap = result.artifacts.caption; - logger.log(`\n✍️ Caption (used):`); - logger.log(` Text: ${cap.text || 'N/A'}`); - if (cap.hashtags && cap.hashtags.length > 0) { - logger.log(` Hashtags: ${cap.hashtags.join(' ')}`); - } - if (cap.disclaimer) { - logger.log(` Disclaimer: ${cap.disclaimer}`); - } - if (cap.length) { - logger.log(` Length: ${cap.length}`); - } - } - - // Caption options - if (result.artifacts.captionOptions && result.artifacts.captionOptions.length > 1) { - logger.log(`\n✍️ Caption Options (${result.artifacts.captionOptions.length} total):`); - result.artifacts.captionOptions.forEach((cap, idx) => { - logger.log(` ${idx + 1}. ${cap.text || 'N/A'}`); - if (cap.hashtags && cap.hashtags.length > 0) { - logger.log(` Hashtags: ${cap.hashtags.join(' ')}`); - } - }); - } - - // Posted links - if (result.artifacts.postedLinks) { - logger.log(`\nπŸ“± Posted Links:`); - if (result.artifacts.postedLinks.telegram) { - logger.log(` Telegram: ${result.artifacts.postedLinks.telegram}`); - } - } - - // Brand information - if (result.artifacts.brandProfile) { - const brand = result.artifacts.brandProfile; - logger.log(`\n🎨 Brand Profile:`); - if (brand.brandAgentId) { - logger.log(` Brand Agent ID: ${brand.brandAgentId}`); - } else { - logger.log(` Brand Name: ${brand.brandName || 'N/A'}`); - logger.log(` Voice: ${brand.voice || brand.personality || 'N/A'}`); - } - } - } - - // Payment details - if (result.payments.length > 0) { - logger.log(`\nπŸ’Έ Payment Details:`); - result.payments.forEach((payment, idx) => { - logger.log(` ${idx + 1}. ${payment.agentId} (${payment.command}): ${payment.amount.toFixed(4)} USDC`); - logger.log(` TX: ${payment.txId}`); - }); - } - - if (result.result) { - logger.log(`\nπŸ“„ Result:`); - logger.log(result.result); - } - } else { - logger.logError(`\n❌ Task failed: ${result.error}`); - if (!loopEnabled) { - process.exit(1); - } - } - - // If looping, wait before next iteration - if (loopEnabled && !shouldStop) { - logger.log(`\n⏳ Waiting ${loopDelaySeconds} seconds before next iteration...`); - await new Promise(resolve => setTimeout(resolve, loopDelaySeconds * 1000)); - } - } while (loopEnabled && !shouldStop); - - if (loopEnabled) { - logger.log('\nβœ… Loop stopped gracefully'); - } - } catch (error) { - logger.logError(`❌ Error: ${error instanceof Error ? error.message : String(error)}`); - process.exit(1); - } - }); -} - diff --git a/examples/marketputer/src/lib/logger.ts b/examples/marketputer/src/lib/logger.ts deleted file mode 100644 index 6a08c42..0000000 --- a/examples/marketputer/src/lib/logger.ts +++ /dev/null @@ -1,255 +0,0 @@ -import ora, { Ora } from 'ora'; - -/** - * Clean, consistent logging system for the agent economy - * Provides only a few log types: section titles, results, and payment details - */ -export class CleanLogger { - private currentSpinner: Ora | null = null; - - /** - * Check if stdin is available for spinner (can change during shutdown) - */ - private isTTYAvailable(): boolean { - try { - return process.stdin.isTTY && process.stdin.readable && !process.stdin.destroyed; - } catch { - return false; - } - } - - /** - * Log a section title (step header) - * Example: "Step 1: Getting focus plan" - * Agent name is shown separately - */ - section(title: string, agent?: string): void { - // Stop any existing spinner - if (this.currentSpinner) { - try { - this.currentSpinner.stop(); - } catch (e) { - // Ignore errors when stopping spinner (e.g., stdin unavailable) - } - this.currentSpinner = null; - } - - // Add spacing before new section - console.log(''); - console.log(`\x1b[1m${title}\x1b[0m`); // Bold - if (agent) { - console.log(` Agent: ${agent}`); - } - } - - /** - * Start a loading spinner for a section - * Returns the spinner so it can be stopped later - */ - startLoading(text: string): Ora | null { - // Stop any existing spinner - if (this.currentSpinner) { - try { - this.currentSpinner.stop(); - } catch (e) { - // Ignore errors when stopping spinner (e.g., stdin unavailable) - } - } - - // Only use spinner if stdin is available (TTY) - if (!this.isTTYAvailable()) { - console.log(` ${text}...`); - return null; - } - - try { - this.currentSpinner = ora({ - text: ` ${text}`, - spinner: 'dots', - stream: process.stdout, // Explicitly use stdout - }).start(); - - return this.currentSpinner; - } catch (e) { - // If spinner fails (e.g., EIO error), fall back to plain text - console.log(` ${text}...`); - return null; - } - } - - /** - * Stop the current spinner and show success - */ - stopLoading(successText?: string): void { - if (this.currentSpinner) { - try { - if (successText) { - this.currentSpinner.succeed(` ${successText}`); - } else { - this.currentSpinner.stop(); - } - } catch (e) { - // Ignore errors when stopping spinner (e.g., stdin unavailable) - if (successText) { - console.log(` βœ… ${successText}`); - } - } - this.currentSpinner = null; - } - } - - /** - * Stop the current spinner and show failure - */ - failLoading(failText: string): void { - if (this.currentSpinner) { - try { - this.currentSpinner.fail(` ${failText}`); - } catch (e) { - // Ignore errors when stopping spinner (e.g., stdin unavailable) - console.log(` ❌ ${failText}`); - } - this.currentSpinner = null; - } - } - - /** - * Log a result (single line output) - * Example: "βœ… Got focus plan: 3 keywords identified" - */ - result(icon: string, message: string): void { - // Stop any spinner first - if (this.currentSpinner) { - try { - this.currentSpinner.stop(); - } catch (e) { - // Ignore errors when stopping spinner (e.g., stdin unavailable) - } - this.currentSpinner = null; - } - - console.log(` ${icon} ${message}`); - } - - /** - * Log payment details in a concise format - */ - payment(details: { - agentId: string; - amount: number; - transactionSignature: string; - txUrl: string; - fromWallet?: string; - fromWalletUrl?: string; - toWallet?: string; - toWalletUrl?: string; - receiptAmount?: number; - }): void { - // Stop any spinner first - if (this.currentSpinner) { - try { - this.currentSpinner.stop(); - } catch (e) { - // Ignore errors when stopping spinner (e.g., stdin unavailable) - } - this.currentSpinner = null; - } - - console.log(` πŸ’Έ ${details.amount.toFixed(4)} USDC β†’ ${details.agentId}`); - console.log(` πŸ”— ${details.txUrl}`); - } - - /** - * Log an info message (for additional context) - */ - info(message: string): void { - console.log(` ℹ️ ${message}`); - } - - /** - * Log a warning - */ - warn(message: string): void { - console.log(` ⚠️ ${message}`); - } - - /** - * Log an error - */ - error(message: string): void { - console.log(` ❌ ${message}`); - } - - /** - * Add spacing between major sections - */ - spacer(): void { - console.log(''); - } - - /** - * Log command input details - */ - commandInput(title: string, details: Record): void { - console.log(`\n πŸ“‹ ${title}:`); - for (const [key, value] of Object.entries(details)) { - if (Array.isArray(value)) { - console.log(` ${key}: ${value.join(', ')}`); - } else if (typeof value === 'object' && value !== null) { - console.log(` ${key}: ${JSON.stringify(value, null, 2).split('\n').join('\n ')}`); - } else { - console.log(` ${key}: ${value}`); - } - } - } - - /** - * Log command result details - */ - commandResult(title: string, response: string, maxPreviewLength: number = 500): void { - console.log(`\n πŸ“‹ ${title}:`); - console.log(` Response length: ${response?.length || 0} characters`); - const preview = response?.substring(0, maxPreviewLength) || 'empty'; - console.log(` Response preview: ${preview}${response && response.length > maxPreviewLength ? '...' : ''}`); - } - - /** - * Log structured JSON data - */ - jsonData(title: string, data: any, isValid: boolean = true): void { - if (isValid) { - console.log(` βœ… ${title}:`, JSON.stringify(data, null, 2).split('\n').map(line => ` ${line}`).join('\n')); - } else { - console.log(` ⚠️ ${title}`); - } - } - - /** - * Log a list of items - */ - list(title: string, items: Array<{ [key: string]: any }>, formatter?: (item: any, idx: number) => string): void { - console.log(` ${title}:`); - items.forEach((item, idx) => { - if (formatter) { - console.log(formatter(item, idx)); - } else { - console.log(` ${idx + 1}. ${JSON.stringify(item)}`); - } - }); - } - - /** - * Log a simple message (for compatibility with console.log) - */ - log(message: string): void { - console.log(message); - } - - /** - * Log an error message (for compatibility with console.error) - */ - logError(message: string): void { - console.error(message); - } -} - diff --git a/examples/marketputer/src/lib/utils.ts b/examples/marketputer/src/lib/utils.ts deleted file mode 100644 index 91ee7a2..0000000 --- a/examples/marketputer/src/lib/utils.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Helper functions for blockchain explorer URLs and network detection - -/** - * Detect if a transaction signature is Base/EVM (hex starting with 0x) or Solana (base58) - */ -function isBaseTransaction(signature: string): boolean { - // Base transaction hashes are hex strings starting with 0x and exactly 66 chars (0x + 64 hex chars) - // Solana signatures are base58 strings, typically 87-88 chars - // Payment headers (base64) are longer and don't start with 0x - if (!signature) return false; - - // Must start with 0x and be exactly 66 characters (0x + 64 hex digits) - if (!signature.startsWith('0x') || signature.length !== 66) { - return false; - } - - // Must be valid hex (only 0-9, a-f, A-F after 0x) - return /^0x[a-fA-F0-9]{64}$/.test(signature); -} - -/** - * Get transaction URL - automatically detects Base vs Solana - */ -export function getTxUrl(signature: string, _network: 'mainnet' | 'devnet' = 'mainnet'): string { - if (isBaseTransaction(signature)) { - return `https://basescan.org/tx/${signature}`; - } - return `https://solscan.io/tx/${signature}`; -} - -/** - * Get account URL - automatically detects Base (0x...) vs Solana (base58) - */ -export function getAccountUrl(address: string, _network: 'mainnet' | 'devnet' = 'mainnet'): string { - if (address.startsWith('0x') && address.length === 42) { - // Base/EVM address - return `https://basescan.org/address/${address}`; - } - // Solana address - return `https://solscan.io/account/${address}`; -} - -/** - * Legacy function for Solana transactions - use getTxUrl instead - */ -export function getSolscanTxUrl(signature: string, _network: 'mainnet' | 'devnet' = 'mainnet'): string { - return getTxUrl(signature, _network); -} - -/** - * Legacy function for Solana accounts - use getAccountUrl instead - */ -export function getSolscanAccountUrl(address: string, _network: 'mainnet' | 'devnet' = 'mainnet'): string { - return getAccountUrl(address, _network); -} - -export function detectNetwork(rpcUrl: string): 'mainnet' | 'devnet' { - if (rpcUrl.includes('devnet')) return 'devnet'; - return 'mainnet'; -} - diff --git a/examples/marketputer/src/orchestrator.ts b/examples/marketputer/src/orchestrator.ts deleted file mode 100644 index 813f531..0000000 --- a/examples/marketputer/src/orchestrator.ts +++ /dev/null @@ -1,1465 +0,0 @@ -import { Connection, Keypair } from '@solana/web3.js'; -import { Memeputer, PromptResult, StatusCheckResult, getUsdcBalance } from '@memeputer/sdk'; -import { OrchestratorConfig, TaskRequest, TaskResult } from './types'; -import { CleanLogger } from './lib/logger'; -import { getTxUrl, getAccountUrl, detectNetwork } from './lib/utils'; - -/** - * Orchestrator - Coordinates and pays multiple specialized agents to complete tasks - * - * This demonstrates an agent-to-agent economy where: - * - The orchestrator has its own wallet with USDC - * - It coordinates a fixed workflow of specialized agents - * - It pays those agents from its own wallet using x402 micropayments - * - The orchestrator tracks spending and manages the budget - */ -export class Orchestrator { - private memeputer: Memeputer; - private wallet: Keypair; - private connection: Connection; - private totalSpent: number = 0; - private agentsHired: string[] = []; - private payments: Array<{ agentId: string; command: string; amount: number; txId: string }> = []; - private network: 'mainnet' | 'devnet'; - private logger: CleanLogger; - private apiBase: string; - - constructor(config: OrchestratorConfig) { - this.memeputer = new Memeputer({ - apiUrl: config.apiBase, - wallet: config.wallet, - connection: config.connection, - verbose: true, // Show x402 protocol details - }); - this.wallet = config.wallet; - this.connection = config.connection; - this.network = detectNetwork(config.connection.rpcEndpoint); - this.logger = new CleanLogger(); - this.apiBase = config.apiBase; // Store apiBase for polling URLs - } - - /** - * Execute a task by hiring and paying other agents - * Fixed task: Find relevant topics and create a meme about them - */ - async executeTask(request: TaskRequest): Promise { - this.totalSpent = 0; - this.agentsHired = []; - this.payments = []; - - const fixedTask = 'Find relevant topics and create a meme about them'; - const brandProfile = request.brandProfile || { - brandName: 'Memeputer', - personality: 'fun, crypto-native, memes', - targetAudience: 'Solana degens', - voice: 'casual, humorous', - denyTerms: [], - }; - - try { - this.logger.section('Orchestrator Agent', `Task: "${fixedTask}" | Budget: ${request.budgetUsdc} USDC`); - - // Step 1: Extract Keywords - this.logger.section('Step 1: Extract Keywords', 'keywordputer'); - this.logger.startLoading('Processing...'); - const focusPlan = await this.whatShouldIFocusOn(fixedTask); - this.logger.stopLoading(); - this.logger.result('βœ…', `Extracted ${focusPlan.keywords?.length || 0} keywords`); - if (focusPlan.keywords && focusPlan.keywords.length > 0) { - this.logger.info(`Keywords: ${focusPlan.keywords.join(', ')}`); - } - - // Step 2: Discover Trends - const trends = await this.discoverTrends(fixedTask, focusPlan.keywords || []); - - // Step 3: Select Best Trend - let selectedTrend: any = null; - if (trends?.items && trends.items.length > 0) { - this.logger.section('Step 3: Select Best Trend', 'trendputer'); - this.logger.startLoading('Processing...'); - selectedTrend = await this.selectBestTrend(trends.items, fixedTask); - this.logger.stopLoading(); - - if (selectedTrend) { - this.logger.result('βœ…', `Selected: "${selectedTrend.title}"`); - if (selectedTrend.summary) { - this.logger.info(` ${selectedTrend.summary.substring(0, 100)}${selectedTrend.summary.length > 100 ? '...' : ''}`); - } - } else { - this.logger.warn('No suitable trends found - proceeding without trend context'); - } - } else { - this.logger.warn('No trends returned - proceeding without trend context'); - } - - // Step 4: Create Creative Brief - const brief = await this.createBrief(selectedTrend, trends, fixedTask, brandProfile); - - // Step 5: Enhance Image Prompt - this.logger.section('Step 5: Enhance Image Prompt', 'promptputer'); - const basePrompt = brief?.brief?.angle || fixedTask; - const imagePrompt = await this.enhanceImagePrompt(basePrompt); - this.logger.result('βœ…', 'Prompt enhanced'); - - // Step 6: Generate Image - const { imageUrl, imageHash, imageStatusUrl } = await this.generateImage(imagePrompt, brandProfile); - - // Wait for image to be ready before proceeding - let readyImageUrl = imageUrl; - if (imageStatusUrl || imageUrl) { - readyImageUrl = await this.waitForImageReady(imageUrl, imageStatusUrl); - } - - // Step 7: Describe Image - let imageDescription: string | null = null; - let imageDescriptionData: any = null; - if (!readyImageUrl) { - this.logger.warn('Skipping image description - no image was generated or image not ready'); - } else { - const descriptionResult = await this.describeImage(readyImageUrl); - imageDescription = descriptionResult.description; - imageDescriptionData = descriptionResult.data; - } - - // Step 8: Write Captions - let caption: string | null = null; - let captionData: any = null; - let captionOptions: any[] = []; - if (!imageDescription) { - this.logger.warn('Skipping caption generation - no image description available'); - } else { - const captionResult = await this.writeCaptions( - imageDescription, - imagePrompt, - trends, - brief, - fixedTask, - brandProfile - ); - caption = captionResult.caption; - captionData = captionResult.captionData; - captionOptions = captionResult.captionOptions; - } - - // Step 9: Broadcast to Telegram - let postedLinks: { telegram?: string } = {}; - if (!imageUrl) { - this.logger.warn('Skipping Telegram post - no image was generated'); - } else if (!caption) { - this.logger.warn('Skipping Telegram post - no caption was generated'); - } else { - postedLinks = await this.broadcastToTelegram( - imageUrl, - caption, - captionData, - captionOptions, - selectedTrend, - trends, - brief, - imagePrompt - ); - } - - // Build and return result - return this.buildTaskResult( - request, - trends, - selectedTrend, - brief, - imageUrl, - imagePrompt, - imageHash, - imageStatusUrl, - imageDescription, - imageDescriptionData, - caption, - captionData, - captionOptions, - postedLinks - ); - } catch (error) { - this.logger.error('\nError during task execution:'); - this.logger.logError(error instanceof Error ? error.stack || error.message : String(error)); - return { - success: false, - totalSpent: this.totalSpent, - agentsHired: [...new Set(this.agentsHired)], - payments: this.payments.map(p => ({ - agentId: p.agentId, - command: p.command || 'unknown', - amount: p.amount, - txId: p.txId, - })), - error: error instanceof Error ? error.message : 'Unknown error', - }; - } - } - - /** - * Step 2: Discover trends using TrendPuter - * Uses the structured discover_trends command for reliable JSON parsing - */ - private async discoverTrends(fixedTask: string, keywords: string[]): Promise { - this.logger.section('Step 2: Discover Trends', 'trendputer'); - - const trendsResult = await this.hireAgentWithCommand('trendputer', 'discover_trends', { - keywords: keywords.length > 0 ? keywords : undefined, - context: fixedTask, - maxResults: 10, - includeHashtags: true, - includeUrl: true, - }); - - // Log the raw command result for debugging - if (trendsResult.response) { - this.logger.commandResult('Trendputer Command Result', trendsResult.response); - try { - const preview = JSON.parse(trendsResult.response); - this.logger.jsonData('Valid JSON structure', { - itemsCount: preview.items?.length || 0, - hasMetadata: !!preview.metadata, - firstItemKeys: preview.items?.[0] ? Object.keys(preview.items[0]) : [] - }); - } catch { - this.logger.warn('Response is not valid JSON'); - } - } - this.logger.spacer(); - - // Parse trends response - guaranteed to be valid JSON - try { - const trends = JSON.parse(trendsResult.response); - this.logger.result('βœ…', `Got ${trends?.items?.length || 0} trends`); - if (trends?.items && trends.items.length > 0) { - this.logger.list('Trends', trends.items, (trend: any, idx: number) => { - const title = ` ${idx + 1}. ${trend.title || 'Untitled'}`; - const summary = trend.summary ? `\n ${trend.summary.substring(0, 80)}${trend.summary.length > 80 ? '...' : ''}` : ''; - return title + summary; - }); - } - return trends; - } catch (error) { - this.logger.error(`Failed to parse trends: ${error instanceof Error ? error.message : error}`); - return { items: [] }; - } - } - - /** - * Step 4: Create creative brief using BriefPuter - */ - private async createBrief( - selectedTrend: any, - trends: any, - fixedTask: string, - brandProfile: any - ): Promise { - this.logger.section('Step 4: Create Creative Brief', 'briefputer'); - - const trendItem = selectedTrend || trends?.items?.[0] || { - title: fixedTask, - summary: fixedTask, - }; - - if (brandProfile.brandAgentId) { - this.logger.info(`Using brand agent: ${brandProfile.brandAgentId}`); - } else if (brandProfile.brandName) { - this.logger.info(`Using brand: ${brandProfile.brandName}`); - } - - const briefPayload: any = { - trendItem, - policy: { - denyTerms: brandProfile.denyTerms || [], - requireDisclaimer: false, - }, - }; - - if (brandProfile.brandAgentId) { - briefPayload.brandAgentId = brandProfile.brandAgentId; - this.logger.info(`Sending brandAgentId to BriefPuter: ${brandProfile.brandAgentId}`); - } else { - briefPayload.brandProfile = brandProfile; - this.logger.info(`Sending brandProfile to BriefPuter: ${brandProfile.brandName || 'Custom'}`); - } - - const briefResult = await this.hireAgentWithCommand('briefputer', 'generate_brief', briefPayload); - - try { - const parsed = JSON.parse(briefResult.response); - const brief = parsed.data; - this.logger.result('βœ…', 'Got creative brief'); - - if (brief?.brief) { - if (brief.brief.angle) { - this.logger.info(` Angle: ${brief.brief.angle.substring(0, 120)}${brief.brief.angle.length > 120 ? '...' : ''}`); - } - if (brief.brief.tone) { - this.logger.info(` Tone: ${brief.brief.tone}`); - } - if (brief.brief.visualStyle && brief.brief.visualStyle.length > 0) { - this.logger.info(` Visual Style: ${brief.brief.visualStyle.join(', ')}`); - } - if (brief.brief.callToAction) { - this.logger.info(` CTA: ${brief.brief.callToAction.substring(0, 80)}${brief.brief.callToAction.length > 80 ? '...' : ''}`); - } - } - return brief; - } catch { - this.logger.warn('Failed to parse brief response'); - return { brief: null }; - } - } - - /** - * Step 6: Generate image using PFPputer - */ - private async generateImage( - enhancedPrompt: string, - brandProfile: any - ): Promise<{ imageUrl: string | null; imageHash: string | null; imageStatusUrl: string | null }> { - this.logger.section('Step 6: Generate Image', 'pfpputer'); - - // Debug: Log brand profile to see what we're working with - this.logger.info(`Brand profile keys: ${Object.keys(brandProfile || {}).join(', ')}`); - this.logger.info(`Has reference_image_urls: ${!!brandProfile?.reference_image_urls}`); - if (brandProfile?.reference_image_urls) { - this.logger.info(`reference_image_urls count: ${brandProfile.reference_image_urls.length}`); - this.logger.info(`reference_image_urls: ${JSON.stringify(brandProfile.reference_image_urls)}`); - } - - // Use pfp_with_reference if reference images are provided, otherwise use regular pfp - // Brand profile uses snake_case to match command parameter names - const imageUrls = brandProfile?.reference_image_urls; - - if (imageUrls && imageUrls.length > 0) { - // Use pfp_with_reference command with structured parameters matching command structure - const pfpParams = { - reference_image_urls: imageUrls, // Array of reference image URLs (required) - prompt: enhancedPrompt, // Optional prompt - }; - this.logger.info(`Using ${imageUrls.length} reference image(s) with pfp_with_reference`); - const imageResult = await this.hireAgentWithCommand('pfpputer', 'pfp_with_reference', pfpParams); - return this.processImageResult(imageResult); - } else { - // Fallback to regular pfp command without reference images - const pfpArgs = [enhancedPrompt]; - const imageResult = await this.hireAgentWithCommand('pfpputer', 'pfp', pfpArgs); - return this.processImageResult(imageResult); - } - } - - private async processImageResult(imageResult: PromptResult): Promise<{ imageUrl: string | null; imageHash: string | null; imageStatusUrl: string | null }> { - // Log the complete PFPputer response structure for debugging - this.logger.info('πŸ“‹ PFPputer Response Structure (x402 spec):'); - this.logger.info(JSON.stringify({ - success: imageResult.success, - response: imageResult.response, - format: imageResult.format, - statusUrl: imageResult.statusUrl, - imageUrl: imageResult.imageUrl, - mediaUrl: imageResult.mediaUrl, - etaSeconds: imageResult.etaSeconds, - transactionSignature: imageResult.transactionSignature, - agentId: imageResult.agentId, - receipt: ((imageResult as any).receipt || imageResult.x402Receipt) ? { - amountPaidUsdc: ((imageResult as any).receipt || imageResult.x402Receipt)!.amountPaidUsdc, - amountPaidMicroUsdc: ((imageResult as any).receipt || imageResult.x402Receipt)!.amountPaidMicroUsdc, - payTo: ((imageResult as any).receipt || imageResult.x402Receipt)!.payTo, - transactionSignature: ((imageResult as any).receipt || imageResult.x402Receipt)!.transactionSignature, - payer: ((imageResult as any).receipt || imageResult.x402Receipt)!.payer, - merchant: ((imageResult as any).receipt || imageResult.x402Receipt)!.merchant, - timestamp: ((imageResult as any).receipt || imageResult.x402Receipt)!.timestamp, - } : undefined, - x402Receipt: imageResult.x402Receipt ? { - amountPaidUsdc: imageResult.x402Receipt.amountPaidUsdc, - amountPaidMicroUsdc: imageResult.x402Receipt.amountPaidMicroUsdc, - payTo: imageResult.x402Receipt.payTo, - transactionSignature: imageResult.x402Receipt.transactionSignature, - payer: imageResult.x402Receipt.payer, - merchant: imageResult.x402Receipt.merchant, - timestamp: imageResult.x402Receipt.timestamp, - } : undefined, - x402Quote: imageResult.x402Quote ? { - amountQuotedUsdc: imageResult.x402Quote.amountQuotedUsdc, - amountQuotedMicroUsdc: imageResult.x402Quote.amountQuotedMicroUsdc, - maxAmountRequired: imageResult.x402Quote.maxAmountRequired, - } : undefined, - }, null, 2)); - this.logger.spacer(); - - let imageStatusUrl: string | null = null; - if (imageResult.statusUrl) { - imageStatusUrl = imageResult.statusUrl; - } - - let imageUrl = imageResult.imageUrl || imageResult.mediaUrl || null; - let imageHash: string | null = null; - - if (!imageUrl) { - try { - const parsed = JSON.parse(imageResult.response); - imageUrl = parsed.data?.imageUrl || parsed.data?.images?.[0]?.image_url || null; - imageHash = parsed.data?.imageHash || null; - } catch { - if (imageResult.response.startsWith('http')) { - imageUrl = imageResult.response.trim(); - } - } - } - - if (!imageUrl && imageResult.statusUrl) { - this.logger.info('Image generation in progress...'); - // Use retryAfterSeconds from initial HTTP 202 response if available (default: 2s) - const retryAfterSeconds = (imageResult as any).retryAfterSeconds; - const statusResult = await this.memeputer.pollStatus(imageResult.statusUrl, { - maxAttempts: 120, - retryAfterSeconds: retryAfterSeconds, // Use from HTTP 202 response - intervalMs: retryAfterSeconds ? retryAfterSeconds * 1000 : 1000, // Fallback to 1s if not provided - onProgress: (attempt: number, _status: StatusCheckResult) => { - const elapsedSeconds = attempt - 1; - if (elapsedSeconds > 0 && elapsedSeconds % 15 === 0) { - this.logger.info(` ⏳ Still processing... (${elapsedSeconds}s elapsed)`); - } - }, - } as any); - imageUrl = statusResult.imageUrl || statusResult.mediaUrl || null; - const statusState = (statusResult as any).state; - const statusCode = (statusResult as any).code; - if (statusResult.status === 'failed' || statusState === 'failed') { - const errorCode = statusCode ? ` (${statusCode})` : ''; - throw new Error(`Image generation failed${errorCode}: ${statusResult.error || 'Unknown error'}`); - } - } - - if (imageUrl) { - this.logger.result('βœ…', `Image generated: ${imageUrl.substring(0, 60)}...`); - } else { - this.logger.warn('No image URL found in response'); - } - - return { imageUrl, imageHash, imageStatusUrl }; - } - - /** - * Wait for image to be ready by polling statusUrl or checking if imageUrl is accessible - */ - private async waitForImageReady(imageUrl: string | null, statusUrl: string | null): Promise { - if (!imageUrl && !statusUrl) { - return null; - } - - // If we have a statusUrl, poll it until image is ready - if (statusUrl) { - this.logger.info('Waiting for image to be ready (polling status URL)...'); - // Note: retryAfterSeconds would come from the initial HTTP 202 response, but we don't have it here - // Default to 2s as per spec - const statusResult = await this.memeputer.pollStatus(statusUrl, { - maxAttempts: 120, - retryAfterSeconds: 2, // Default per spec - intervalMs: 2000, // Default 2s per spec - onProgress: (attempt: number, status: StatusCheckResult) => { - const elapsedSeconds = attempt - 1; - const statusState = (status as any).state; - if (elapsedSeconds > 0 && elapsedSeconds % 15 === 0) { - this.logger.info(` ⏳ Still waiting for image... (${elapsedSeconds}s elapsed, status: ${status.status || statusState})`); - } - }, - } as any); - - const statusState = (statusResult as any).state; - if (statusResult.status === 'completed' || statusState === 'succeeded') { - const readyUrl = statusResult.imageUrl || statusResult.mediaUrl || imageUrl; - if (readyUrl) { - this.logger.result('βœ…', 'Image is ready'); - return readyUrl; - } - } else if (statusResult.status === 'failed' || statusState === 'failed') { - const statusCode = (statusResult as any).code; - const errorCode = statusCode ? ` (${statusCode})` : ''; - this.logger.error(`Image generation failed${errorCode}: ${statusResult.error || 'Unknown error'}`); - return null; - } - } - - // If no statusUrl or statusUrl didn't return imageUrl, check if imageUrl is accessible - if (imageUrl) { - this.logger.info('Checking if image URL is accessible...'); - const axios = (await import('axios')).default; - const maxAttempts = 30; // 30 seconds max - const delayMs = 1000; - - for (let attempt = 1; attempt <= maxAttempts; attempt++) { - try { - const response = await axios.head(imageUrl, { - validateStatus: (status) => status < 500, // Don't throw on 4xx - timeout: 5000, - }); - - if (response.status === 200) { - this.logger.result('βœ…', 'Image URL is accessible'); - return imageUrl; - } - - // If 404, wait and retry - if (response.status === 404) { - if (attempt < maxAttempts) { - await new Promise(resolve => setTimeout(resolve, delayMs)); - continue; - } - } - } catch (error) { - // Network error or timeout - wait and retry - if (attempt < maxAttempts) { - await new Promise(resolve => setTimeout(resolve, delayMs)); - continue; - } - } - } - - this.logger.warn('Image URL not accessible after waiting'); - } - - return imageUrl; // Return original URL even if not accessible (let describe_image handle it) - } - - /** - * Step 7: Describe image using ImageDescripterputer - */ - private async describeImage(imageUrl: string): Promise<{ description: string | null; data: any }> { - this.logger.section('Step 7: Describe Image', 'imagedescripterputer'); - - try { - const descriptionResult = await this.hireAgentWithCommand('imagedescripterputer', 'describe_image', { - imageUrl: imageUrl, - detailLevel: 'detailed', - }); - - let statusUrl: string | null = null; - - if (descriptionResult.statusUrl) { - statusUrl = descriptionResult.statusUrl; - } - - if (!statusUrl) { - try { - const parsed = JSON.parse(descriptionResult.response); - statusUrl = parsed.data?.statusUrl || null; - } catch { - // Response might not be JSON yet - } - } - - if (statusUrl) { - // Use SDK's pollStatus method for proper async handling - this.logger.info('Image description in progress (async operation)...'); - // Use retryAfterSeconds from initial HTTP 202 response if available (default: 2s) - const retryAfterSeconds = (descriptionResult as any).retryAfterSeconds; - const statusResult = await this.memeputer.pollStatus(statusUrl, { - maxAttempts: 120, - retryAfterSeconds: retryAfterSeconds, // Use from HTTP 202 response - intervalMs: retryAfterSeconds ? retryAfterSeconds * 1000 : 1000, // Fallback to 1s if not provided - onProgress: (attempt: number, status: StatusCheckResult) => { - const elapsedSeconds = attempt - 1; - const statusState = (status as any).state; - if (elapsedSeconds > 0 && elapsedSeconds % 15 === 0) { - this.logger.info(` ⏳ Still processing... (${elapsedSeconds}s elapsed, status: ${status.status || statusState})`); - } - }, - } as any); - - const statusState = (statusResult as any).state; - if (statusResult.status === 'completed' || statusState === 'succeeded') { - // Extract description from status result - let description: string | null = null; - let data: any = null; - - // Try to parse response if it's a string - if (statusResult.message) { - try { - const parsed = JSON.parse(statusResult.message); - description = parsed.description || parsed.data?.description || statusResult.message; - data = parsed; - } catch { - description = statusResult.message; - data = { description }; - } - } - - // Also check imageUrl/mediaUrl in case description is there - if (!description && (statusResult.imageUrl || statusResult.mediaUrl)) { - // If we got a URL, might need to fetch it or it might be the description - description = statusResult.imageUrl || statusResult.mediaUrl || null; - } - - if (description) { - this.logger.result('βœ…', 'Got image description'); - const preview = description.substring(0, 120); - this.logger.info(` ${preview}${description.length > 120 ? '...' : ''}`); - } else { - this.logger.warn('Status completed but no description found'); - } - - return { description, data: data || { description } }; - } else if (statusResult.status === 'failed' || statusState === 'failed') { - const statusCode = (statusResult as any).code; - const errorCode = statusCode ? ` (${statusCode})` : ''; - throw new Error(`Image description failed${errorCode}: ${statusResult.error || 'Unknown error'}`); - } else { - this.logger.warn(`Unexpected status: ${statusResult.status || statusState}`); - return { description: null, data: null }; - } - } else { - try { - const parsed = JSON.parse(descriptionResult.response); - const description = parsed.data?.description || null; - - if (description) { - this.logger.result('βœ…', 'Got image description'); - const preview = description.substring(0, 120); - this.logger.info(` ${preview}${description.length > 120 ? '...' : ''}`); - } else { - this.logger.warn('No description found in response'); - } - return { description, data: parsed }; - } catch (parseError) { - if (descriptionResult.response.includes('Analyzing') || descriptionResult.response.includes('processing')) { - this.logger.warn('Image description appears async but no statusUrl found'); - this.logger.info('Check backend implementation - statusUrl should be returned'); - } else { - this.logger.warn(`Failed to parse image description: ${parseError instanceof Error ? parseError.message : parseError}`); - } - return { description: null, data: null }; - } - } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - this.logger.error(`Failed to describe image: ${errorMessage}`); - return { description: null, data: null }; - } - } - - /** - * Step 8: Write captions using Captionputer - */ - private async writeCaptions( - imageDescription: string, - imagePrompt: string | null, - trends: any, - brief: any, - fixedTask: string, - brandProfile: any - ): Promise<{ caption: string | null; captionData: any; captionOptions: any[] }> { - this.logger.section('Step 8: Write Captions', 'captionputer'); - - const captionTrendItem = trends?.items?.[0] || { - title: fixedTask, - summary: fixedTask, - }; - - const numVariants = 3; - - try { - const captionPayload: any = { - imageDescription: imageDescription, - imagePrompt: imagePrompt || null, - trendItem: captionTrendItem, - brief: brief?.brief || null, - numVariants, - }; - - if (brandProfile.captionPuterOptions?.promptTemplate) { - captionPayload.customInstructions = brandProfile.captionPuterOptions.promptTemplate; - } - - if (brandProfile.brandAgentId) { - captionPayload.brandAgentId = brandProfile.brandAgentId; - } else { - captionPayload.brandProfile = brandProfile; - } - - const captionResult = await this.hireAgentWithCommand('captionputer', 'generate_captions', captionPayload); - - try { - const parsed = JSON.parse(captionResult.response); - const captions = parsed.data?.captions || []; - - this.logger.info(`CaptionPuter response structure: ${JSON.stringify(Object.keys(parsed))}`); - this.logger.info(`Found ${captions.length} caption(s) in response`); - - if (captions.length > 0) { - const captionOptions = captions; - const caption = captions[0]?.text || null; - const captionData = captions[0] || null; - - this.logger.result('βœ…', `Got ${captions.length} caption option${captions.length > 1 ? 's' : ''}`); - - captions.forEach((cap: any, idx: number) => { - const preview = cap.text?.substring(0, 80) || 'N/A'; - this.logger.info(` Option ${idx + 1}: ${preview}${cap.text && cap.text.length > 80 ? '...' : ''}`); - }); - - if (captions.length === 1 && numVariants > 1) { - this.logger.warn(`⚠️ Requested ${numVariants} captions but only received 1`); - } - - return { caption, captionData, captionOptions }; - } else { - this.logger.warn('No captions returned in response'); - this.logger.info(`Full response: ${JSON.stringify(parsed).substring(0, 500)}`); - return { caption: null, captionData: null, captionOptions: [] }; - } - } catch (parseError) { - this.logger.warn(`Failed to parse caption response: ${parseError instanceof Error ? parseError.message : parseError}`); - this.logger.info(`Raw response: ${captionResult.response.substring(0, 500)}`); - return { caption: null, captionData: null, captionOptions: [] }; - } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - this.logger.error(`Failed to generate captions: ${errorMessage}`); - return { caption: null, captionData: null, captionOptions: [] }; - } - } - - /** - * Step 9: Broadcast to Telegram using Broadcastputer - */ - private async broadcastToTelegram( - imageUrl: string, - caption: string, - captionData: any, - captionOptions: any[], - selectedTrend: any, - trends: any, - brief: any, - imagePrompt: string | null - ): Promise<{ telegram?: string }> { - this.logger.section('Step 9: Broadcast to Telegram', 'broadcastputer'); - - const telegramChatId = process.env.TELEGRAM_CHAT_ID || process.env.MEMEPUTER_TELEGRAM_CHAT_ID; - - if (!telegramChatId) { - this.logger.warn('Skipping Telegram post (chat ID not configured)'); - return {}; - } - - this.logger.info(`Posting to Telegram (Chat ID: ${telegramChatId})`); - - const captionsToShow = captionOptions.length > 0 - ? captionOptions.map((cap: any) => ({ - text: cap.text || null, - hashtags: cap.hashtags || [], - })) - : caption - ? [{ text: caption, hashtags: captionData?.hashtags || [] }] - : null; - - const enhancedCaption = this.buildEnhancedCaption( - captionsToShow, - selectedTrend || trends?.items?.[0] || null, - brief?.brief || null, - imagePrompt || null - ); - - try { - const telegramResult = await this.hireAgentWithCommand('broadcastputer', 'post_telegram', { - chatId: telegramChatId, - caption: enhancedCaption, - imageUrl: imageUrl || '', - }); - - try { - let responseText = telegramResult.response.trim(); - if (responseText.startsWith('```')) { - responseText = responseText.replace(/^```(?:json)?\s*\n?/, '').replace(/\n?```\s*$/, ''); - } - - const parsed = JSON.parse(responseText); - const messageLink = parsed.data?.messageLink || null; - - if (messageLink && typeof messageLink === 'string') { - this.logger.result('βœ…', `Posted to Telegram: ${messageLink}`); - return { telegram: messageLink }; - } else { - const responseStr = JSON.stringify(parsed); - const urlMatch = responseStr.match(/https:\/\/t\.me\/[^\s"']+/); - if (urlMatch) { - this.logger.result('βœ…', `Posted to Telegram: ${urlMatch[0]}`); - return { telegram: urlMatch[0] }; - } else { - this.logger.warn('Posted to Telegram but no message link found in response'); - return {}; - } - } - } catch { - const urlMatch = telegramResult.response.match(/https:\/\/t\.me\/[^\s"']+/); - if (urlMatch) { - this.logger.result('βœ…', `Posted to Telegram: ${urlMatch[0]}`); - return { telegram: urlMatch[0] }; - } else if (telegramResult.response.includes('http')) { - const link = telegramResult.response.trim(); - this.logger.result('βœ…', `Posted to Telegram: ${link}`); - return { telegram: link }; - } else { - this.logger.warn('Unexpected response format from BroadcastPuter'); - return {}; - } - } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - this.logger.error(`Failed to post to Telegram: ${errorMessage}`); - if (errorMessage.includes('402') || errorMessage.includes('payment')) { - this.logger.info('This appears to be a payment issue - check BroadcastPuter\'s pricing'); - } else if (errorMessage.includes('500') || errorMessage.includes('Internal')) { - this.logger.info('This appears to be a backend error - BroadcastPuter may be having issues'); - } - return {}; - } - } - - /** - * Build the final task result object - */ - private buildTaskResult( - request: TaskRequest, - trends: any, - selectedTrend: any, - brief: any, - imageUrl: string | null, - imagePrompt: string | null, - imageHash: string | null, - imageStatusUrl: string | null, - imageDescription: string | null, - imageDescriptionData: any, - caption: string | null, - captionData: any, - captionOptions: any[], - postedLinks: { telegram?: string } - ): TaskResult { - const result: string[] = []; - if (trends) { - result.push(`Trends found: ${trends.items?.length || 0} items`); - } - if (brief) { - result.push(`Brief created: ${brief.brief?.angle || 'N/A'}`); - } - if (imageUrl) { - result.push(`πŸ–ΌοΈ Image: ${imageUrl}`); - } else { - result.push(`⚠️ Image: Not generated or URL not found`); - } - if (caption) { - result.push(`Caption: ${caption}`); - } - if (postedLinks.telegram) { - result.push(`πŸ“± Telegram: ${postedLinks.telegram}`); - } - - this.logger.spacer(); - this.logger.section('βœ… Task Completed', ''); - this.logger.result('πŸ’°', `Total spent: ${this.totalSpent.toFixed(4)} USDC`); - this.logger.result('πŸ’΅', `Remaining budget: ${(request.budgetUsdc - this.totalSpent).toFixed(4)} USDC`); - this.logger.result('πŸ‘₯', `Agents hired: ${this.agentsHired.length}`); - this.logger.result('πŸ’Έ', `Payments made: ${this.payments.length}`); - - const finalSelectedTrend = selectedTrend || trends?.items?.[0] || null; - - return { - success: true, - totalSpent: this.totalSpent, - agentsHired: [...new Set(this.agentsHired)], - payments: this.payments.map(p => ({ - agentId: p.agentId, - command: p.command || 'unknown', - amount: p.amount, - txId: p.txId, - })), - result: result.join('\n'), - artifacts: { - trends: trends ? { - items: trends.items || [], - selectedTrend: finalSelectedTrend ? { - id: finalSelectedTrend.id, - title: finalSelectedTrend.title, - summary: finalSelectedTrend.summary, - score: finalSelectedTrend.score, - hashtags: finalSelectedTrend.hashtags, - canonicalUrl: finalSelectedTrend.canonicalUrl, - } : undefined, - } : null, - brief: brief?.brief ? { - angle: brief.brief.angle, - tone: brief.brief.tone, - visualStyle: brief.brief.visualStyle, - callToAction: brief.brief.callToAction, - negativeConstraints: brief.brief.negativeConstraints, - } : null, - imageGeneration: imageUrl || imagePrompt ? { - prompt: imagePrompt || undefined, - imageUrl: imageUrl || undefined, - imageHash: imageHash || undefined, - statusUrl: imageStatusUrl || undefined, - seed: undefined, - guidance: undefined, - } : undefined, - imageDescription: imageDescription ? { - description: imageDescription, - style: imageDescriptionData?.style || null, - composition: imageDescriptionData?.composition || null, - details: imageDescriptionData?.details || null, - } : null, - caption: captionData || caption ? { - text: caption || captionData?.text || null, - hashtags: captionData?.hashtags || [], - disclaimer: captionData?.disclaimer || null, - length: captionData?.length || null, - } : null, - captionOptions: captionOptions.length > 0 ? captionOptions.map((cap: any) => ({ - text: cap.text || null, - hashtags: cap.hashtags || [], - disclaimer: cap.disclaimer || null, - length: cap.length || null, - })) : null, - postedLinks: Object.keys(postedLinks).length > 0 ? postedLinks : null, - brandProfile: request.brandProfile || null, - }, - }; - } - - /** - * Hire an agent with a structured command and track payment - * - * This demonstrates agent-to-agent economy: - * - The orchestrator agent pays from the provided wallet - * - Each payment is tracked and deducted from the agent's budget - * - The actual payment amount comes from the 402 receipt/quote - */ - private async hireAgentWithCommand( - agentId: string, - command: string, - payload: any - ): Promise { - this.logger.startLoading(`Calling ${agentId}...`); - - try { - // Call agent via SDK with structured command (payment handled internally by SDK) - // SDK will automatically detect if command needs JSON format or CLI format - const result = await this.memeputer.command(agentId, command, payload); - - this.logger.stopLoading(`Completed ${agentId}`); - - // Track payment if transaction occurred - if (result.transactionSignature) { - // Use standardized receipt field with fallback to legacy x402Receipt - const receipt = ((result as any).receipt || result.x402Receipt) as any; - - // Receipt should always be present after payment - quote is only in 402 response before payment - if (!receipt?.amountPaidUsdc) { - throw new Error(`Payment transaction exists but no receipt amount found for ${agentId}`); - } - - // Use receipt amount (actual paid) - this is what we actually spent - const actualAmount = receipt.amountPaidUsdc; - - this.totalSpent += actualAmount; - this.agentsHired.push(agentId); - this.payments.push({ - agentId, - command, - amount: actualAmount, - txId: result.transactionSignature, - }); - - // Log payment details - use receipt amount (actual paid) for display - const payer = receipt?.payer || this.wallet.publicKey.toString(); - const merchant = receipt?.merchant || receipt?.payTo || ''; - const paymentAmount = receipt?.amountPaidUsdc || actualAmount; - - this.logger.payment({ - agentId, - amount: paymentAmount, - transactionSignature: result.transactionSignature, - txUrl: getTxUrl(result.transactionSignature, this.network), - fromWallet: payer, - fromWalletUrl: getAccountUrl(payer, this.network), - toWallet: merchant, - toWalletUrl: merchant ? getAccountUrl(merchant, this.network) : undefined, - receiptAmount: receipt?.amountPaidUsdc, - }); - } - - return result; - } catch (error) { - this.logger.failLoading(`Failed to pay ${agentId}`); - throw error; - } - } - - /** - * Step 3: Select Best Trend - * Uses Trendputer to evaluate trends and select the highest quality option - * Uses the structured select_best_trend command for reliable JSON parsing - */ - private async selectBestTrend(trends: any[], task: string): Promise { - if (!trends || trends.length === 0) { - return null; - } - - // If only one trend, use it (but could still evaluate quality) - if (trends.length === 1) { - return trends[0]; - } - - // Extract trend titles for simple format (backend expects trendTitles or trends) - const trendTitles = trends.map(t => t.title || t.name || String(t)).filter(Boolean); - - const commandParams = { - trendTitles: trendTitles, // Simple format - array of strings - trends: trends, // Full objects format (takes precedence if backend supports it) - task: task, - criteria: ['relevance', 'quality', 'engagement'], - returnFormat: 'index' as const, - includeReasoning: true, - }; - - // Log input parameters - this.logger.commandInput('Trendputer Command Input', { - Task: `"${task}"`, - Trends: `${trends.length} trends to evaluate`, - TrendList: trends.map((t, idx) => `${idx + 1}. "${t.title || 'Untitled'}" (score: ${t.score || 'N/A'})`).join('\n'), - Criteria: commandParams.criteria.join(', ') - }); - - try { - // Step 3: Select Best Trend - Uses Trendputer command to evaluate trends and select highest quality option - const result = await this.hireAgentWithCommand('trendputer', 'select_best_trend', commandParams); - - // Log the raw command result for debugging - if (result.response) { - this.logger.commandResult('Trendputer Command Result', result.response); - try { - const preview = JSON.parse(result.response); - this.logger.jsonData('Valid JSON structure', { - hasData: !!preview.data, - selectedIndex: preview.data?.selectedIndex, - hasSelectedTrend: !!preview.data?.selectedTrend, - hasReasoning: !!preview.data?.reasoning - }); - } catch { - this.logger.warn('Response is not valid JSON'); - } - } - this.logger.spacer(); - - // Parse response - guaranteed format: { "data": { selectedIndex: number, selectedTrend?: {...}, reasoning?: string } } - // Handle potential double-wrapping: { "data": { "data": { ... } } } - try { - const parsed = JSON.parse(result.response); - const data = parsed.data?.data || parsed.data; // Handle double-wrapping if present - const selectedIndex = data?.selectedIndex; - - if (selectedIndex !== undefined && selectedIndex >= 0 && selectedIndex < trends.length) { - const selectedTrend = trends[selectedIndex]; - this.logger.result('βœ…', `Selected: "${selectedTrend.title || 'Untitled'}"`); - if (data?.reasoning) { - this.logger.info(` Reasoning: ${data.reasoning}`); - } - return selectedTrend; - } else if (selectedIndex === -1 || selectedIndex === null) { - this.logger.warn('No suitable trend selected by Trendputer'); - return null; - } else { - this.logger.error(`Invalid selectedIndex: ${selectedIndex} (expected 0-${trends.length - 1})`); - return null; - } - } catch (parseError) { - this.logger.error(`Failed to parse trend selection: ${parseError instanceof Error ? parseError.message : parseError}`); - // Fallback to heuristic-based selection if parsing fails - this.logger.warn('Falling back to heuristic selection'); - return this.selectBestTrendHeuristic(trends, task); - } - } catch (error) { - // Fallback to heuristic-based selection if AI evaluation fails - this.logger.warn('Trend selection failed, using heuristic fallback'); - - return this.selectBestTrendHeuristic(trends, task); - } - } - - /** - * Fallback heuristic-based trend selection - * Used if AI-based evaluation fails - */ - private selectBestTrendHeuristic(trends: any[], task: string): any | null { - const taskKeywords = task.toLowerCase().split(/\s+/).filter(w => w.length > 3); - - const scoredTrends = trends.map(trend => { - let score = trend.score || 0; - - const trendText = `${trend.title || ''} ${trend.summary || ''}`.toLowerCase(); - - // Boost for task keyword matches (general relevance) - const keywordMatches = taskKeywords.filter(kw => trendText.includes(kw)).length; - score += keywordMatches * 5; - - // Boost if has URL (more credible) - if (trend.canonicalUrl) { - score += 5; - } - - // Boost if has hashtags (more engagement potential) - if (trend.hashtags && trend.hashtags.length > 0) { - score += trend.hashtags.length * 2; - } - - // Penalize very low-quality or spammy trends - // Only penalize if trend has very low base score AND no relevance - if ((trend.score || 0) < 5 && keywordMatches === 0) { - score -= 10; // Light penalty for completely irrelevant low-score trends - } - - return { trend, score }; - }); - - // Sort by score descending - scoredTrends.sort((a, b) => b.score - a.score); - - // Log top 3 candidates - this.logger.list('πŸ“Š Top trend candidates (heuristic)', scoredTrends.slice(0, 3), (item, idx) => - ` ${idx + 1}. "${item.trend.title}" (score: ${item.score.toFixed(1)})` - ); - - // Only use trend if score is above threshold - const bestTrend = scoredTrends[0]; - const threshold = 10; // Minimum score to consider - - if (bestTrend.score >= threshold) { - this.logger.result('βœ…', `Selected trend with score ${bestTrend.score.toFixed(1)} (threshold: ${threshold})`); - return bestTrend.trend; - } else { - this.logger.warn(`Best trend score ${bestTrend.score.toFixed(1)} below threshold ${threshold} - rejecting all trends`); - return null; - } - } - - /** - * Step 1: Extract Keywords - * Uses Keywordputer to extract relevant keywords from the task - * Uses the structured keywords command for reliable JSON parsing - */ - private async whatShouldIFocusOn(task: string): Promise<{ - focusArea: string; - keywords: string[]; - topics: string[]; - reasoning: string; - }> { - const commandParams = { - text: task, - context: 'Creating content for Solana community', - targetAudience: 'Solana degens', - contentGoal: 'meme', - maxKeywords: 10, - }; - - // Log input parameters - this.logger.commandInput('Keywordputer Command Input', { - Task: `"${task}"`, - Context: commandParams.context, - 'Target Audience': commandParams.targetAudience, - 'Content Goal': commandParams.contentGoal, - 'Max Keywords': commandParams.maxKeywords - }); - - try { - // Step 1: Extract Keywords using Keywordputer command - const result = await this.hireAgentWithCommand('keywordputer', 'keywords', commandParams); - - // Log the raw command result for debugging - if (result.response) { - this.logger.commandResult('Keywordputer Command Result', result.response); - try { - const preview = JSON.parse(result.response); - this.logger.jsonData('Valid JSON structure', { - hasData: !!preview.data, - keywordsCount: preview.data?.keywords?.length || 0, - keywords: preview.data?.keywords || [] - }); - } catch { - this.logger.warn('Response is not valid JSON'); - } - } - this.logger.spacer(); - - // Parse response - guaranteed format: { "data": { "keywords": [...] } } - try { - const parsed = JSON.parse(result.response); - const keywords = parsed.data?.keywords || []; - - this.logger.result('βœ…', `Extracted ${keywords.length} keywords`); - if (keywords.length > 0) { - this.logger.info(` Keywords: ${keywords.join(', ')}`); - } - - return { - focusArea: task, - keywords: keywords, - topics: keywords.slice(0, 3), // Use first 3 keywords as topics - reasoning: `Extracted ${keywords.length} keywords from task`, - }; - } catch (parseError) { - this.logger.error(`Failed to parse keywords: ${parseError instanceof Error ? parseError.message : parseError}`); - // Fallback: Extract keywords from task - const taskKeywords = task.toLowerCase().split(/\s+/).filter(w => w.length > 3); - return { - focusArea: task, - keywords: taskKeywords.slice(0, 5), - topics: ['crypto', 'tech'], - reasoning: 'Using task keywords as fallback', - }; - } - } catch (error) { - this.logger.warn('Keyword extraction failed, using fallback'); - // Fallback: Extract keywords from task - const taskKeywords = task.toLowerCase().split(/\s+/).filter(w => w.length > 3); - return { - focusArea: task, - keywords: taskKeywords.slice(0, 5), - topics: ['crypto', 'tech'], - reasoning: 'Using task keywords as fallback', - }; - } - } - - /** - * Step 5: Enhance Image Prompt - * Uses Promptputer to refine and enhance the image generation prompt with quality modifiers - * Uses the structured enhance_prompt command for reliable JSON parsing - */ - private async enhanceImagePrompt(basePrompt: string): Promise { - try { - // Step 5: Enhance Image Prompt - Uses Promptputer to enhance prompt with quality modifiers - const commandParams = { - basePrompt: basePrompt, - qualityModifiers: ['8K', 'cinematic', 'artstation', 'highly detailed', 'professional quality'], - style: 'artistic', - detailLevel: 'high', - includeTechnicalSpecs: true, - tone: 'dramatic', - }; - - // Log input parameters - this.logger.commandInput('Promptputer Command Input', { - 'Base Prompt': `${basePrompt.substring(0, 100)}${basePrompt.length > 100 ? '...' : ''}`, - 'Quality Modifiers': commandParams.qualityModifiers.join(', '), - 'Style': commandParams.style, - 'Detail Level': commandParams.detailLevel, - 'Tone': commandParams.tone - }); - - const result = await this.hireAgentWithCommand('promptputer', 'enhance_prompt', commandParams); - - // Log the raw command result for debugging - if (result.response) { - this.logger.commandResult('Promptputer Command Result', result.response); - try { - const preview = JSON.parse(result.response); - this.logger.jsonData('Valid JSON structure', { - hasEnhancedPrompt: !!preview.enhancedPrompt, - enhancedPromptLength: preview.enhancedPrompt?.length || 0, - modifiersApplied: preview.modifiersApplied?.length || 0, - style: preview.style, - detailLevel: preview.detailLevel, - }); - } catch { - this.logger.warn('Response is not valid JSON'); - } - } - this.logger.spacer(); - - if (result.transactionSignature) { - // Use standardized receipt field with fallback to legacy x402Receipt - const receipt = ((result as any).receipt || result.x402Receipt) as any; - const actualAmount = receipt?.amountPaidUsdc || 0.01; - this.totalSpent += actualAmount; - this.payments.push({ - agentId: 'promptputer', - command: 'enhance_prompt', - amount: actualAmount, - txId: result.transactionSignature, - }); - - // Log payment - const payer = receipt?.payer || this.wallet.publicKey.toString(); - const merchant = receipt?.merchant || receipt?.payTo || ''; - const paymentAmount = result.x402Quote?.amountQuotedUsdc || actualAmount; - - this.logger.payment({ - agentId: 'promptputer', - amount: paymentAmount, - transactionSignature: result.transactionSignature, - txUrl: getTxUrl(result.transactionSignature, this.network), - fromWallet: payer, - fromWalletUrl: getAccountUrl(payer, this.network), - toWallet: merchant, - toWalletUrl: merchant ? getAccountUrl(merchant, this.network) : undefined, - receiptAmount: receipt?.amountPaidUsdc, - }); - } - - // Parse JSON response - guaranteed to be valid JSON - try { - const parsed = JSON.parse(result.response); - // Handle nested structure: { data: { enhancedPrompt: "...", ... } } - const data = parsed.data || parsed; - const enhancedPrompt = data.enhancedPrompt || parsed.enhancedPrompt || result.response; - this.logger.result('βœ…', 'Got enhanced prompt'); - - // Log the enhanced prompt output (human-readable) - this.logger.info('πŸ“ Enhanced Prompt Output:'); - this.logger.info(`${enhancedPrompt.substring(0, 200)}${enhancedPrompt.length > 200 ? '...' : ''}`); - const modifiersApplied = data.modifiersApplied || parsed.modifiersApplied; - if (modifiersApplied && modifiersApplied.length > 0) { - this.logger.info(`Modifiers Applied: ${modifiersApplied.join(', ')}`); - } - this.logger.spacer(); - - return enhancedPrompt.trim(); - } catch (parseError) { - // If JSON parsing fails, fall back to raw response - this.logger.warn('Failed to parse enhanced prompt as JSON, using raw response'); - this.logger.warn('Using raw response (not JSON):'); - this.logger.info(`${result.response.substring(0, 200)}${result.response.length > 200 ? '...' : ''}`); - this.logger.spacer(); - return result.response.trim(); - } - } catch (error) { - this.logger.warn(`Prompt enhancement failed, using base prompt: ${error instanceof Error ? error.message : 'Unknown error'}`); - return basePrompt; // Fallback to base prompt - } - } - - /** - * Build enhanced Telegram caption with context (trend, brief, prompt) and all caption options - * Caption options appear FIRST so they're easy to see - */ - private buildEnhancedCaption( - captionOptions: Array<{ text?: string; hashtags?: string[] }> | null, - trend: any | null, - brief: any | null, - prompt: string | null - ): string { - let enhanced = ''; - - // Add all caption options FIRST (at the top) - if (captionOptions && captionOptions.length > 0) { - enhanced += `✍️ Caption Options:\n\n`; - captionOptions.forEach((cap, idx) => { - const captionText = cap.text || 'N/A'; - enhanced += `Caption ${idx + 1}:\n`; - enhanced += `${captionText}\n`; - if (cap.hashtags && cap.hashtags.length > 0) { - enhanced += `🏷️ ${cap.hashtags.join(' ')}\n`; - } - enhanced += `\n`; - }); - } else { - enhanced += `✍️ Caption:\n`; - enhanced += `No captions generated\n\n`; - } - - // Add separator - enhanced += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`; - - // Add trend context - if (trend) { - enhanced += `πŸ“° Trend: ${trend.title || 'N/A'}\n\n`; - if (trend.summary) { - const summary = trend.summary.substring(0, 200); - enhanced += `πŸ“ ${summary}${trend.summary.length > 200 ? '...' : ''}\n`; - } - - // Add source link if available - if (trend.canonicalUrl) { - enhanced += `πŸ”— Source\n`; - } - - enhanced += `\n`; - } - - // Add creative angle if available - if (brief?.angle) { - enhanced += `πŸ’‘ Creative Angle:\n`; - enhanced += `${brief.angle.substring(0, 200)}${brief.angle.length > 200 ? '...' : ''}\n\n`; - } - - // Note: Image prompt is intentionally omitted as it's too long and gets cut off - // The trend title above provides better context - - return enhanced; - } - - /** - * Poll image description status URL until description is ready - */ - private async pollImageDescription(statusUrl: string, maxAttempts: number = 120, delayMs: number = 1000): Promise { - const axios = (await import('axios')).default; - let attempts = 0; - - while (attempts < maxAttempts) { - await new Promise(resolve => setTimeout(resolve, delayMs)); - attempts++; - - try { - const response = await axios.get(statusUrl, { - validateStatus: (status) => status < 500, // Don't throw on 4xx - }); - - const data = response.data; - - // Handle nested response structure: { data: { status, description, ... }, meta: {...} } - const actualData = data.data || data; // Support both nested and flat structures - - // Check if completed - use actualData for nested responses - // Accept both "completed" and "success" statuses - if (actualData.status === 'completed' || actualData.status === 'success' || actualData.description) { - this.logger.result('βœ…', 'Got image description'); - // Return the actual data as JSON string for parsing - return JSON.stringify(actualData); - } - - // Check if failed - if (actualData.status === 'failed' || actualData.error) { - this.logger.error(`Image description failed: ${actualData.error || 'Unknown error'}`); - return null; - } - - // Still processing - minimal logging (only every 15 seconds) - if (attempts % 15 === 0) { - this.logger.info(`Still processing... (${attempts}s)`); - } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - this.logger.warn(`Polling error (attempt ${attempts}): ${errorMessage}`); - if (attempts < 5) { - // Retry on early errors - continue; - } - this.logger.warn(`Connection issue, retrying... (${errorMessage})`); - if (attempts >= maxAttempts) { - this.logger.error(`Failed to poll image description after ${maxAttempts} attempts`); - return null; - } - } - } - - this.logger.warn(`Polling timeout after ${maxAttempts} seconds`); - return null; - } - - /** - * Get current balance (for monitoring) - */ - async getBalance(): Promise { - return getUsdcBalance(this.connection, this.wallet); - } -} - diff --git a/examples/marketputer/src/types.ts b/examples/marketputer/src/types.ts deleted file mode 100644 index 4c73448..0000000 --- a/examples/marketputer/src/types.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { z } from 'zod'; -import { Connection, Keypair } from '@solana/web3.js'; - -// BrandProfile schema -// If brandAgentId is provided, all other fields come from the agent's profile in Memeputer -// If brandAgentId is not provided, brandName and voice are required -export const BrandProfileSchema = z.object({ - brandAgentId: z.string().optional(), // If provided, use brand agent's profile (all other fields ignored) - brandName: z.string().optional(), // Required if brandAgentId not provided - voice: z.string().optional(), // Required if brandAgentId not provided - personality: z.string().optional(), // Alternative to voice - targetAudience: z.string().optional(), - styleKeywords: z.array(z.string()).optional(), // Not needed if brandAgentId provided - logoUrl: z.string().nullable().optional(), - allowTerms: z.array(z.string()).optional(), // Not needed if brandAgentId provided - denyTerms: z.array(z.string()).optional(), // Not needed if brandAgentId provided - disclaimer: z.string().nullable().optional(), - primaryColor: z.string().nullable().optional(), - emojiPack: z.array(z.string()).optional(), // Not needed if brandAgentId provided - reference_image_urls: z.array(z.string().url()).optional(), // Reference image URLs for PFPputer (snake_case, matches command structure) - captionPuterOptions: z.object({ - promptTemplate: z.string().optional(), // Custom instructions for CaptionPuter prompt template - }).optional(), // CaptionPuter-specific options -}).passthrough().refine( - (data) => data.brandAgentId || (data.brandName && (data.voice || data.personality)), - { - message: "Either brandAgentId or both brandName and voice/personality must be provided", - } -); - -export type BrandProfile = z.infer; - -// Orchestrator types -export interface OrchestratorConfig { - wallet: Keypair; - connection: Connection; - apiBase: string; -} - -export interface TaskRequest { - task: string; - budgetUsdc: number; - brandProfile?: BrandProfile; // Optional brand profile for voice/style -} - -export interface TaskResult { - success: boolean; - totalSpent: number; - agentsHired: string[]; - payments: Array<{ - agentId: string; - command: string; - amount: number; - txId: string; - }>; - result?: string; - artifacts?: { - // Trend information - trends?: { - items: Array<{ - id?: string; - title?: string; - summary?: string; - score?: number; - hashtags?: string[]; - canonicalUrl?: string | null; - }>; - selectedTrend?: { - id?: string; - title?: string; - summary?: string; - score?: number; - hashtags?: string[]; - canonicalUrl?: string | null; - }; - } | null; - // Brief information - brief?: { - angle?: string; - tone?: string; - visualStyle?: string[]; - callToAction?: string; - negativeConstraints?: string[]; - } | null; - // Image generation details - imageGeneration?: { - prompt?: string; - imageUrl?: string | null; - imageHash?: string | null; - statusUrl?: string | null; - seed?: number | null; - guidance?: number | null; - } | null; - // Image description from ImageDescripterPuter - imageDescription?: { - description?: string; - style?: any | null; - composition?: any | null; - details?: any | null; - } | null; - // Caption information - caption?: { - text?: string; - hashtags?: string[]; - disclaimer?: string | null; - length?: string; - } | null; - // Multiple caption options - captionOptions?: Array<{ - text?: string; - hashtags?: string[]; - disclaimer?: string | null; - length?: string; - }> | null; - // Social media posts - postedLinks?: { - telegram?: string; - } | null; - // Brand information used - brandProfile?: BrandProfile | null; - }; - error?: string; -} - diff --git a/examples/marketputer/tsconfig.json b/examples/marketputer/tsconfig.json deleted file mode 100644 index b432dc9..0000000 --- a/examples/marketputer/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": ["ES2020"], - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "moduleResolution": "node" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} - diff --git a/examples/marketputer/vitest.config.ts b/examples/marketputer/vitest.config.ts deleted file mode 100644 index 29752c2..0000000 --- a/examples/marketputer/vitest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - test: { - include: ['src/**/__tests__/**/*.test.ts'], - exclude: ['node_modules', 'dist'], - environment: 'node', - globals: true, - }, -}); - diff --git a/package.json b/package.json index 48249e3..9aee4c0 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,88 @@ { - "name": "memeputer-monorepo", - "version": "0.0.0", - "private": true, - "description": "Monorepo for Memeputer packages", + "name": "memeputer", + "version": "2.0.0", + "license": "MIT", + "type": "module", + "description": "Official TypeScript SDK + CLI for the Memeputer agent chat platform.", + "homepage": "https://docs.memeputer.com", + "repository": { + "type": "git", + "url": "git+https://github.com/rawgroundbeef/memeputer.git" + }, + "bugs": { + "url": "https://github.com/rawgroundbeef/memeputer/issues" + }, + "keywords": [ + "memeputer", + "solana", + "x402", + "meteora", + "sdk", + "agent", + "chat" + ], + "files": [ + "dist", + "README.md", + "CHANGELOG.md", + "LICENSE" + ], + "publishConfig": { + "provenance": true, + "access": "public" + }, + "bin": { + "memeputer": "./dist/cli.mjs" + }, + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", "scripts": { - "build": "pnpm -r build", - "test": "pnpm -r test", - "lint": "pnpm -r lint", - "changeset": "changeset", - "version": "changeset version", - "release": "pnpm build && changeset publish", - "clean": "pnpm -r clean", - "link:cli": "pnpm --filter memeputer build && cd packages/cli && pnpm link --global", - "prepare": "husky", - "generate-solana-wallet": "tsx scripts/generate-solana-wallet.ts", - "generate-base-wallet": "tsx scripts/generate-base-wallet.ts" + "build": "tsup && node scripts/post-build.mjs", + "typecheck": "tsc --noEmit", + "test": "pnpm build && vitest run --passWithNoTests", + "pack:dry": "npm pack --dry-run --json ." + }, + "peerDependencies": { + "@coral-xyz/anchor": "^0.32.1", + "@openfacilitator/sdk": "1.0.0", + "@solana/web3.js": "^1.98.0", + "socket.io-client": "^4.8.3" + }, + "peerDependenciesMeta": { + "socket.io-client": { + "optional": true + } + }, + "dependencies": { + "bs58": "^6.0.0", + "tweetnacl": "^1.0.3" }, "devDependencies": { - "@changesets/cli": "^2.27.0", - "@solana/web3.js": "^1.98.4", - "@typescript-eslint/eslint-plugin": "^7.0.0", - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.57.0", - "ethers": "^6.13.0", - "husky": "^9.1.7", - "lint-staged": "^16.2.6", - "prettier": "^3.3.0", - "tsx": "^4.20.6", - "typescript": "^5.9.0" + "@coral-xyz/anchor": "^0.32.1", + "@openfacilitator/sdk": "1.0.0", + "@solana/web3.js": "^1.98.0", + "@types/node": "^22.0.0", + "socket.io-client": "^4.8.3", + "tsup": "^8.0.0", + "typescript": "^5.6.0", + "vitest": "^2.0.0" }, "engines": { - "node": ">=18.0.0", - "pnpm": ">=8.0.0" + "node": ">=22", + "pnpm": ">=10.16" }, - "packageManager": "pnpm@8.15.0" + "packageManager": "pnpm@10.16.0" } diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore deleted file mode 100644 index d0f706d..0000000 --- a/packages/cli/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules/ -dist/ -.env -.DS_Store -*.log -*.tgz - diff --git a/packages/cli/.npmignore b/packages/cli/.npmignore deleted file mode 100644 index d33a995..0000000 --- a/packages/cli/.npmignore +++ /dev/null @@ -1,7 +0,0 @@ -src/ -tsconfig.json -.env -*.log -node_modules/ -.DS_Store - diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md deleted file mode 100644 index 81f87be..0000000 --- a/packages/cli/CHANGELOG.md +++ /dev/null @@ -1,76 +0,0 @@ -# memeputer - -## 1.5.0 - -### Minor Changes - -- Updated dependencies - - @memeputer/sdk@1.6.0 - - **Improvements:** - - Fixed double `/x402` prefix issue in resource URLs - - Added URL construction debug logging - - Improved error handling for agent requests - - Support for `discover_trends` command - -## 1.4.0 - -### Minor Changes - -- Updated dependencies - - @memeputer/sdk@1.5.0 - - **New Features:** - - Base/EVM wallet support with auto-detection - - EIP-3009 payment format for gasless Base transactions - - Base USDC balance checking - - Improved transaction hash and payer address handling - -## 1.3.0 - -### Minor Changes - -- Add multi-chain support for Solana and Base (EVM) chains - - Add chain parameter to SDK and CLI with auto-detection from environment - - Update API URL structure to /x402/{chain}/{agentId} - - Implement EVM payment transaction creation using ethers.js - - Add wallet generation scripts for both Solana and Base - - Support MEMEPUTER_CHAIN environment variable - - Update all tests for multi-chain URL structure - - **Breaking Changes:** - - API endpoint structure changed from `/x402/interact` to `/x402/{chain}/{agentId}` - - `interact()` method signature updated to accept `Keypair | any` for multi-chain wallet support - - **Client-side ready:** - - βœ… Solana payments working in production - - βœ… Base payment creation implemented (pending backend support) - -### Patch Changes - -- Updated dependencies - - @memeputer/sdk@1.4.0 - -## 1.2.0 - -### Minor Changes - -- Migrate to unified domain agents.memeputer.com/x402 - - BREAKING CHANGE: The default API URL has changed from `agents.api.memeputer.com` to `agents.memeputer.com/x402`. The endpoint structure has also changed - agent IDs are now in the URL path (e.g., `POST /x402/{agentId}`) instead of the request body. - - **For users:** - - Update your configuration files to use the new domain - - Environment variable `MEMEPUTER_API_URL` should be set to `https://agents.memeputer.com/x402` - - Local development now uses `http://localhost:3006/x402` - - **Changes:** - - Default API URLs updated across SDK and CLI - - Agent ID now in URL path instead of request body - - Endpoint paths simplified (no duplicate `/x402` segments) - - All tests updated to reflect new structure - -### Patch Changes - -- Updated dependencies - - @memeputer/sdk@1.3.0 diff --git a/packages/cli/README.md b/packages/cli/README.md deleted file mode 100644 index 043998a..0000000 --- a/packages/cli/README.md +++ /dev/null @@ -1,325 +0,0 @@ -# Memeputer CLI πŸ€– - -Pay and interact with AI agents from your terminal using x402 micropayments. - -**✨ Pay $0 in gas fees** - All transactions use PayAI Facilitator! - -## Installation - -```bash -npm install -g memeputer -``` - -## Quick Start - -```bash -# List available agents -memeputer agents - -# Ask an agent (auto-pays via x402) -memeputer ask memeputer "What can you do?" - -# Execute custom commands -memeputer ask rawgroundbeefbot "/ping" - -# Check your wallet balance -memeputer balance --wallet ~/.config/solana/id.json -``` - -## Commands - -### `memeputer agents` - -List all available agents with pricing and categories. - -**Example:** - -```bash -memeputer agents -# Shows table of agents with prices - -memeputer agents --json -# Output in JSON format for scripting -``` - -### `memeputer ask ` - -Interact with any agent. Payment happens automatically via x402 protocol. - -**Examples:** - -```bash -# Chat with memeputer -memeputer ask memeputer "Tell me a joke" -w ~/.config/solana/id.json - -# Get trading analysis -memeputer ask tradeputer "What's the sentiment on SOL?" - -# Voice generation -memeputer ask veoputer "Create an upbeat podcast intro" -``` - -**Options:** - -- `-w, --wallet ` - Path to Solana wallet keypair file -- `--json` - Output in JSON format -- `-q, --quiet` - Suppress progress output - -**Custom Commands:** - -Agents can have custom commands that you can execute by starting your message with `/command`: - -```bash -# Execute the /ping command -memeputer ask rawgroundbeefbot "/ping" - -# Custom commands can have parameters -memeputer ask someagent "/weather san francisco" -``` - -### `memeputer balance` - -Check your wallet's USDC balance. - -**Example:** - -```bash -memeputer balance -w ~/.config/solana/id.json - -# Output: -# πŸ’° Wallet Balance -# Address: 7zH2...pump -# Balance: 10.50 USDC -``` - -## Configuration - -Create `~/.memeputerrc` for default settings: - -```json -{ - "wallet": "/path/to/wallet.json", - "network": "mainnet-beta", - "chain": "solana", - "apiUrl": "https://agents.memeputer.com/x402" -} -``` - -## Environment Variables - -- `MEMEPUTER_WALLET` - Default wallet path -- `MEMEPUTER_API_URL` - API endpoint (default: https://agents.memeputer.com/x402) -- `MEMEPUTER_CHAIN` - Blockchain to use: `solana` (default) or `base` -- `SOLANA_RPC_URL` - Custom Solana RPC endpoint (default: Helius) - -## Multi-Chain Support - -Switch between Solana and Base easily: - -```bash -# Use Solana (default) -export MEMEPUTER_CHAIN=solana -memeputer ask memeputer "hello" - -# Use Base -export MEMEPUTER_CHAIN=base -memeputer ask memeputer "hello" -``` - -Or in your `.memeputerrc`: - -```json -{ - "chain": "base", - "apiUrl": "https://agents.memeputer.com/x402" -} -``` - -## How It Works - -The CLI uses the **x402 micropayment protocol**: - -1. πŸ” Loads your Solana wallet from file -2. πŸ“‘ Calls agent API (first call returns 402 Payment Required) -3. πŸ’Έ Creates USDC payment transaction automatically -4. βœ… Signs transaction with your wallet -5. πŸ“€ Sends payment via **PayAI Facilitator** (you pay $0 gas!) -6. πŸ€– Returns AI-generated result - -All payments are instant and on-chain. No accounts, no subscriptions. - -## Requirements - -- **Node.js 18+** -- **Solana wallet with USDC** - - Get a wallet at https://phantom.app - - Export keypair to JSON file - - Add USDC to your wallet - -## Getting a Wallet - -### Option 1: Phantom Wallet (Browser) - -1. Install Phantom browser extension -2. Create wallet -3. Go to Settings β†’ Export Private Key -4. Save as JSON file - -### Option 2: Solana CLI - -```bash -# Install Solana CLI -sh -c "$(curl -sSfL https://release.solana.com/stable/install)" - -# Generate new wallet -solana-keygen new --outfile ~/.config/solana/id.json - -# Check address -solana address - -# Add USDC to this address -``` - -## Example Workflows - -### Execute a custom command - -```bash -memeputer ask rawgroundbeefbot "/ping" -w ~/.config/solana/id.json - -# Custom commands can do anything: -# - Generate images -# - Call external APIs -# - Return formatted data -# - Trigger webhooks -``` - -### Get trading analysis - -```bash -memeputer ask tradeputer "analyze BTC/USD" -w ./wallet.json - -# Agent responds with: -# - Market analysis -# - Price predictions -# - Trading signals -``` - -### Check balance before paying - -```bash -memeputer balance -w ~/.config/solana/id.json -# Balance: 10.50 USDC βœ… (enough for ~100 interactions) - -memeputer ask memeputer "What's the weather?" -w ~/.config/solana/id.json -# Payment auto-sent, response received! -``` - -### JSON output for scripting - -```bash -# Get response as JSON -result=$(memeputer ask rawgroundbeefbot "/ping" --json) - -# Extract response -echo $result | jq -r '.response' - -# Parse custom command output -echo $result | jq -r '.format' # text, image, video, etc. -``` - -## Pricing - -Agent prices are set by their creators. Typical prices: - -- **General chat**: $0.01-$0.05 per message -- **Image generation**: $0.05-$0.15 per image -- **Voice/audio**: $0.05-$0.10 per generation -- **Trading signals**: $0.10-$0.50 per analysis - -**You pay exactly the agent price + $0.00 gas fees!** (PayAI covers gas) - -## Troubleshooting - -### "Insufficient USDC balance" - -Your wallet needs USDC (not SOL) to pay agents. - -1. Get USDC on an exchange (Coinbase, Binance) -2. Withdraw to your Solana wallet address -3. Run `memeputer balance` to verify - -### "Wallet file not found" - -Provide full path to your wallet JSON file: - -```bash -memeputer ask memeputer "hi" --wallet /full/path/to/wallet.json -``` - -### "Network timeout" - -Solana RPC can be slow. Try again or set custom RPC: - -```bash -export SOLANA_RPC_URL="https://api.mainnet-beta.solana.com" -``` - -### "Agent not found" - -Check available agents: - -```bash -memeputer agents -# Lists all marketplace-enabled agents -``` - -## Development - -```bash -# Clone repo -git clone https://github.com/memeputer/memeputer - -# Install dependencies -cd apps/cli -npm install - -# Run in dev mode -npm run dev - -# Build -npm run build - -# Test locally -npm link -memeputer agents -``` - -## Publishing - -```bash -# Build for production -npm run build - -# Publish to npm -npm publish - -# Users can then install: -npm install -g memeputer-cli -``` - -## Support - -- **Website:** https://memeputer.com -- **Marketplace:** https://agents.memeputer.com -- **API Docs:** https://agents.memeputer.com/docs -- **Discord:** https://discord.gg/memeputer -- **Twitter:** @MemeputerAI - -## License - -MIT - ---- - -**Made with πŸ’œ by the Memeputer team** diff --git a/packages/cli/package.json b/packages/cli/package.json deleted file mode 100644 index e6de73d..0000000 --- a/packages/cli/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "memeputer", - "version": "1.5.1", - "description": "CLI tool for interacting with Memeputer AI agents via x402 payments", - "bin": { - "memeputer": "./dist/index.js" - }, - "main": "dist/index.js", - "type": "module", - "scripts": { - "dev": "echo 'βœ… CLI is ready! Run: cd apps/cli && tsx src/index.ts -- agents'", - "test": "vitest", - "test:run": "vitest run", - "test:ui": "vitest --ui", - "build": "tsc", - "start": "node dist/index.js", - "prepublishOnly": "npm run build" - }, - "keywords": [ - "memeputer", - "ai", - "agents", - "cli", - "x402", - "solana", - "micropayments" - ], - "author": "Memeputer", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/memeputer/memeputer-oss.git", - "directory": "packages/cli" - }, - "homepage": "https://memeputer.com", - "bugs": { - "url": "https://github.com/memeputer/memeputer-oss/issues" - }, - "dependencies": { - "@memeputer/sdk": "workspace:*", - "@solana/web3.js": "^1.98.4", - "axios": "^1.7.7", - "bs58": "^6.0.0", - "chalk": "^5.3.0", - "commander": "^11.1.0", - "dotenv": "^16.3.1", - "ora": "^8.0.1" - }, - "devDependencies": { - "@types/node": "^20.10.6", - "@vitest/ui": "^4.0.8", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^4.0.8" - }, - "engines": { - "node": ">=18.0.0" - } -} diff --git a/packages/cli/src/__tests__/config.test.ts b/packages/cli/src/__tests__/config.test.ts deleted file mode 100644 index 3b3f5a7..0000000 --- a/packages/cli/src/__tests__/config.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; -import { existsSync, writeFileSync, unlinkSync, mkdirSync, rmSync } from 'fs'; -import { join } from 'path'; -import { homedir } from 'os'; - -describe('CLI Configuration', () => { - describe('Environment Variables', () => { - let originalEnv: NodeJS.ProcessEnv; - - beforeEach(() => { - originalEnv = { ...process.env }; - }); - - afterEach(() => { - process.env = originalEnv; - }); - - it('should read MEMEPUTER_WALLET from environment', () => { - process.env.MEMEPUTER_WALLET = '/test/wallet.json'; - expect(process.env.MEMEPUTER_WALLET).toBe('/test/wallet.json'); - }); - - it('should read MEMEPUTER_API_URL from environment', () => { - process.env.MEMEPUTER_API_URL = 'https://test.api.com'; - expect(process.env.MEMEPUTER_API_URL).toBe('https://test.api.com'); - }); - - it('should read SOLANA_RPC_URL from environment', () => { - process.env.SOLANA_RPC_URL = 'https://test.rpc.com'; - expect(process.env.SOLANA_RPC_URL).toBe('https://test.rpc.com'); - }); - }); - - describe('Default Paths', () => { - it('should have default Solana config path', () => { - const defaultPath = join(homedir(), '.config', 'solana', 'id.json'); - expect(typeof defaultPath).toBe('string'); - expect(defaultPath).toContain('.config'); - expect(defaultPath).toContain('solana'); - }); - - it('should have default memeputerrc path', () => { - const rcPath = join(homedir(), '.memeputerrc'); - expect(typeof rcPath).toBe('string'); - expect(rcPath).toContain('.memeputerrc'); - }); - }); -}); - diff --git a/packages/cli/src/__tests__/formatting.test.ts b/packages/cli/src/__tests__/formatting.test.ts deleted file mode 100644 index eb88774..0000000 --- a/packages/cli/src/__tests__/formatting.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - formatAgent, - formatPublicKey, - formatUsdc, - formatError, - formatSuccess, -} from '../utils/formatting'; - -describe('CLI Formatting Utilities', () => { - describe('formatAgent', () => { - it('should format agent name with emoji', () => { - const result = formatAgent('memeputer'); - expect(result).toContain('memeputer'); - expect(result).toContain('πŸ€–'); - }); - }); - - describe('formatPublicKey', () => { - it('should truncate long public keys', () => { - const longKey = 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ'; - const result = formatPublicKey(longKey); - expect(result).toContain('G31J'); - expect(result).toContain('...'); - expect(result.length).toBeLessThan(longKey.length); - }); - - it('should handle short keys', () => { - const shortKey = 'ABC123'; - const result = formatPublicKey(shortKey); - expect(result).toContain('ABC123'); - }); - }); - - describe('formatUsdc', () => { - it('should format USDC amounts with 2 decimals by default', () => { - expect(formatUsdc(1.5)).toContain('1.50'); - expect(formatUsdc(1.5)).toContain('USDC'); - }); - - it('should format small amounts with more decimals', () => { - expect(formatUsdc(0.001)).toContain('0.001'); - expect(formatUsdc(0.0001)).toContain('0.0001'); - }); - - it('should format zero correctly', () => { - expect(formatUsdc(0)).toContain('0.00'); - }); - - it('should format large amounts correctly', () => { - expect(formatUsdc(1000)).toContain('1000.00'); - expect(formatUsdc(1000.5)).toContain('1000.50'); - }); - }); - - describe('formatError', () => { - it('should format error messages', () => { - const result = formatError('Something went wrong'); - expect(result).toContain('Something went wrong'); - expect(result).toContain('❌'); - }); - }); - - describe('formatSuccess', () => { - it('should format success messages', () => { - const result = formatSuccess('Operation completed'); - expect(result).toContain('Operation completed'); - expect(result).toContain('βœ…'); - }); - }); -}); - diff --git a/packages/cli/src/__tests__/wallet.test.ts b/packages/cli/src/__tests__/wallet.test.ts deleted file mode 100644 index 73b36af..0000000 --- a/packages/cli/src/__tests__/wallet.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { Keypair } from '@solana/web3.js'; - -describe('Wallet Utilities', () => { - describe('Keypair Generation', () => { - it('should generate valid Solana keypairs', () => { - const keypair = Keypair.generate(); - - expect(keypair).toBeDefined(); - expect(keypair.publicKey).toBeDefined(); - expect(keypair.secretKey).toBeDefined(); - expect(keypair.publicKey.toBase58()).toBeTruthy(); - expect(keypair.secretKey.length).toBe(64); - }); - - it('should generate unique keypairs', () => { - const keypair1 = Keypair.generate(); - const keypair2 = Keypair.generate(); - - expect(keypair1.publicKey.toBase58()).not.toBe(keypair2.publicKey.toBase58()); - }); - }); - - describe('Public Key Validation', () => { - it('should validate public key format', () => { - const keypair = Keypair.generate(); - const pubkey = keypair.publicKey.toBase58(); - - // Solana public keys are base58 encoded, typically 32-44 chars - expect(pubkey.length).toBeGreaterThan(30); - expect(pubkey.length).toBeLessThan(50); - expect(/^[1-9A-HJ-NP-Za-km-z]+$/.test(pubkey)).toBe(true); // Base58 alphabet - }); - }); -}); - diff --git a/packages/cli/src/commands/agents.ts b/packages/cli/src/commands/agents.ts deleted file mode 100644 index 7d972bf..0000000 --- a/packages/cli/src/commands/agents.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Command } from "commander"; -import ora from "ora"; -import { loadConfig } from "../lib/config.js"; -import { AgentsApiClient } from "@memeputer/sdk"; -import { - formatError, - formatTable, - formatPrice, - formatAgent, -} from "../utils/formatting.js"; - -export function createAgentsCommand(): Command { - return new Command("agents") - .description("List all available AI agents") - .option("--json", "Output in JSON format") - .action(async (options) => { - try { - const config = loadConfig(); - const apiUrl = config.apiUrl!; - - const spinner = options.json ? null : ora("Fetching agents...").start(); - const client = new AgentsApiClient(apiUrl); - const agents = await client.listAgents(); - - if (spinner) { - spinner.stop(); - } - - if (options.json) { - console.log(JSON.stringify(agents, null, 2)); - } else { - console.log(); - console.log("πŸ€– Available Memeputer Agents"); - console.log(); - - formatTable( - ["Agent", "Description", "Price", "Category"], - agents.map((agent) => [ - formatAgent(agent.name), - agent.description.slice(0, 50) + - (agent.description.length > 50 ? "..." : ""), - formatPrice(agent.price), - agent.category, - ]), - ); - - console.log(); - console.log("πŸ’‘ Usage:"); - console.log( - ' memeputer pfp "your prompt" --wallet ~/.config/solana/id.json', - ); - console.log(' memeputer prompt veOputer "your message"'); - console.log(); - } - } catch (error: any) { - console.error(formatError(error.message || "Failed to fetch agents")); - process.exit(1); - } - }); -} diff --git a/packages/cli/src/commands/balance.ts b/packages/cli/src/commands/balance.ts deleted file mode 100644 index aff2553..0000000 --- a/packages/cli/src/commands/balance.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Command } from "commander"; -import { Connection } from "@solana/web3.js"; -import ora from "ora"; -import chalk from "chalk"; -import { loadConfig, getDefaultWalletPath } from "../lib/config.js"; -import { loadWallet, formatPublicKey } from "../lib/wallet.js"; -import { getUsdcBalance } from "@memeputer/sdk"; -import { - formatError, - formatSuccess, - formatPrice, -} from "../utils/formatting.js"; - -export function createBalanceCommand(): Command { - return new Command("balance") - .description("Check your wallet's USDC balance") - .option("-w, --wallet ", "Path to Solana wallet keypair") - .option("--json", "Output in JSON format") - .action(async (options) => { - try { - const config = loadConfig(); - const rpcUrl = config.rpcUrl!; - const walletPath = - options.wallet || config.wallet || getDefaultWalletPath(); - - if (!walletPath) { - console.error( - formatError( - "No wallet specified. Use --wallet or set MEMEPUTER_WALLET", - ), - ); - process.exit(1); - } - - // Load wallet - const spinner = options.json ? null : ora("Loading wallet...").start(); - const wallet = loadWallet(walletPath); - - if (spinner) { - spinner.text = "Checking USDC balance..."; - } - - // Connect and get balance - const connection = new Connection(rpcUrl, "confirmed"); - const balance = await getUsdcBalance(connection, wallet); - - if (spinner) { - spinner.stop(); - } - - // Output results - if (options.json) { - console.log( - JSON.stringify( - { - wallet: wallet.publicKey.toBase58(), - balance, - currency: "USDC", - }, - null, - 2, - ), - ); - } else { - console.log(); - console.log(formatSuccess("πŸ’° Wallet Balance")); - console.log(); - console.log( - ` Address: ${formatPublicKey(wallet.publicKey.toBase58())}`, - ); - console.log(` Balance: ${chalk.bold.green(formatPrice(balance))}`); - console.log(); - - if (balance < 0.1) { - console.log( - chalk.yellow( - "⚠️ Low balance! You may not have enough USDC to interact with agents.", - ), - ); - console.log( - chalk.gray( - " Most agents cost between $0.01-$0.10 per interaction.", - ), - ); - console.log(); - } - } - } catch (error: any) { - console.error(formatError(error.message || "Failed to check balance")); - process.exit(1); - } - }); -} diff --git a/packages/cli/src/commands/command.ts b/packages/cli/src/commands/command.ts deleted file mode 100644 index 94a87ae..0000000 --- a/packages/cli/src/commands/command.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { Command } from "commander"; -import { Connection } from "@solana/web3.js"; -import ora, { Ora } from "ora"; -import chalk from "chalk"; -import fs from "fs"; -import path from "path"; -import { Memeputer, StatusCheckResult } from "@memeputer/sdk"; -import { loadConfig, getDefaultWalletPath } from "../lib/config.js"; -import { loadWallet, formatPublicKey } from "../lib/wallet.js"; -import { formatInfo } from "../utils/formatting.js"; - -export const commandCommand = new Command("command") - .description("Execute a custom command on an agent") - .argument("", "Agent username or ID") - .argument("", "Command name (without the / prefix)") - .allowUnknownOption() // Allow command-specific options like --sources, --max-items, etc. - .option("-w, --wallet ", "Path to Solana wallet keypair") - .option( - "-d, --download ", - "Download media to specified path (for async commands)", - ) - .option("--json", "Output in JSON format") - .option("-q, --quiet", "Suppress progress output") - .action( - async ( - agentName: string, - command: string, - options: { - wallet?: string; - download?: string; - json?: boolean; - quiet?: boolean; - [key: string]: any; // Allow dynamic options - }, - ) => { - const quiet = options.quiet || options.json; - let spinner: Ora | null = null; - - try { - const config = loadConfig(); - const apiUrl = config.apiUrl!; - const rpcUrl = config.rpcUrl!; - const walletPath = - options.wallet || config.wallet || getDefaultWalletPath(); - - if (!walletPath) { - console.error( - chalk.red( - "No wallet specified. Use --wallet or set MEMEPUTER_WALLET", - ), - ); - process.exit(1); - } - - const agentId = agentName.toLowerCase(); - - // Load wallet - spinner = quiet ? null : ora("Loading wallet...").start(); - const wallet = loadWallet(walletPath); - if (spinner) { - spinner.succeed( - `Wallet loaded: ${formatPublicKey(wallet.publicKey.toBase58())}`, - ); - } - - // Connect to Solana - spinner = quiet ? null : ora("Connecting to Solana...").start(); - const connection = new Connection(rpcUrl, "confirmed"); - if (spinner) { - spinner.succeed("Connected to Solana"); - } - - // Extract command-specific options from process.argv - // Format: memeputer command agent command-name --param1 value1 --param2 value2 - const cliOptionNames = ['wallet', 'w', 'download', 'd', 'json', 'quiet', 'q']; - const commandParams: string[] = []; - - // Get the raw args after 'command agent command-name' - const args = process.argv.slice(process.argv.indexOf(command) + 1); - - // Parse named parameters: --param value or --param=value - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - - // Skip CLI options (handled by Commander) - if (cliOptionNames.some(opt => arg === `--${opt}` || arg === `-${opt}`)) { - i++; // Skip the value too - continue; - } - - // Handle --param=value format - if (arg.startsWith('--') && arg.includes('=')) { - const [key, value] = arg.substring(2).split('='); - if (!cliOptionNames.includes(key)) { - commandParams.push(value); - } - } - // Handle --param value format - else if (arg.startsWith('--') && !arg.includes('=')) { - const key = arg.substring(2); - if (!cliOptionNames.includes(key) && i + 1 < args.length) { - const value = args[i + 1]; - // Check if next arg is not another option - if (!value.startsWith('-')) { - commandParams.push(value); - i++; // Skip the value - } - } - } - // Handle positional arguments (for backward compatibility) - else if (!arg.startsWith('-')) { - commandParams.push(arg); - } - } - - // Build the message with slash prefix - // Format: /command param1 param2 param3 - const message = commandParams.length > 0 - ? `/${command} ${commandParams.join(" ")}` - : `/${command}`; - - spinner = quiet - ? null - : ora(`Executing /${command} on ${agentId}...`).start(); - - const memeputer = new Memeputer({ - apiUrl, - rpcUrl, - wallet, - connection, - }); - - const result = await memeputer.command({ - agentId, - command, - params: commandParams, - }); - - // Check if this is an async operation - if (result.statusUrl) { - if (spinner) { - spinner.text = `Operation started. Waiting for completion...`; - } - - // Poll for completion - const finalResult = await memeputer.pollStatus(result.statusUrl, { - intervalMs: 5000, - maxAttempts: 60, - onProgress: (attempt: number, status: StatusCheckResult) => { - if (spinner && attempt > 1) { - const elapsed = attempt * 5; - spinner.text = `Waiting... (${elapsed}s, status: ${status.status})`; - } - }, - }); - - if (spinner) { - if (finalResult.status === "completed") { - spinner.succeed("Operation completed!"); - } else { - spinner.fail( - `Operation ${finalResult.status}: ${finalResult.error || "Unknown error"}`, - ); - } - } - - // Update result with final data - result.response = finalResult.message || result.response; - result.imageUrl = finalResult.imageUrl || result.imageUrl; - result.mediaUrl = finalResult.mediaUrl || result.mediaUrl; - - // Download if requested - if (options.download && (result.imageUrl || result.mediaUrl)) { - spinner = quiet ? null : ora("Downloading media...").start(); - const mediaUrl = result.imageUrl || result.mediaUrl; - - const response = await fetch(mediaUrl!); - const buffer = await response.arrayBuffer(); - - // Add timestamp to filename to prevent overwriting - const timestamp = new Date() - .toISOString() - .replace(/[:.]/g, "-") - .slice(0, -5); - const ext = path.extname(mediaUrl!) || ".png"; - const basename = path.basename(options.download, ext); - const dirname = path.dirname(options.download); - const timestampedPath = path.join( - dirname, - `${basename}-${timestamp}${ext}`, - ); - - fs.writeFileSync(timestampedPath, Buffer.from(buffer)); - if (spinner) { - spinner.succeed( - chalk.green(`βœ“ Media downloaded to: ${timestampedPath}`), - ); - } - } - } else if (spinner) { - spinner.succeed(chalk.green("βœ“ Command executed")); - } - - // Display result - if (!quiet) { - console.log(""); - console.log(chalk.bold("Response:")); - console.log(result.response); - - if (result.imageUrl) { - console.log(""); - console.log(chalk.blue(`πŸ–ΌοΈ Image: ${result.imageUrl}`)); - } - - if (result.mediaUrl) { - console.log(""); - console.log(chalk.blue(`πŸ“ Media: ${result.mediaUrl}`)); - } - - if (result.transactionSignature) { - console.log(""); - console.log( - formatInfo( - `πŸ’³ Transaction: ${chalk.underline(`https://solscan.io/tx/${result.transactionSignature}`)}`, - ), - ); - console.log( - chalk.green( - "πŸ’š You paid $0 in gas fees (PayAI Facilitator covered it)!", - ), - ); - } - - console.log(""); - } else if (options.json) { - console.log(JSON.stringify(result, null, 2)); - } - } catch (error: any) { - if (spinner) { - spinner.fail(chalk.red("βœ— Command failed")); - } - - if (!quiet) { - console.error(""); - console.error(chalk.red("Error:"), error.message); - - if (error.response?.data) { - console.error( - chalk.gray(JSON.stringify(error.response.data, null, 2)), - ); - } - } - - process.exit(1); - } - }, - ); diff --git a/packages/cli/src/commands/prompt.ts b/packages/cli/src/commands/prompt.ts deleted file mode 100644 index 36837eb..0000000 --- a/packages/cli/src/commands/prompt.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { Command } from "commander"; -import { Connection } from "@solana/web3.js"; -import ora from "ora"; -import chalk from "chalk"; -import { loadConfig, getDefaultWalletPath } from "../lib/config.js"; -import { loadWallet, formatPublicKey } from "../lib/wallet.js"; -import { Memeputer } from "@memeputer/sdk"; -import { - formatSuccess, - formatError, - formatInfo, - formatAgent, -} from "../utils/formatting.js"; - -export function createPromptCommand(): Command { - return new Command("prompt") - .description("Prompt any agent with a message (pays via x402 with $0 gas fees!)") - .argument("", "Agent ID (e.g., memeputer, tradeputer, pfpputer)") - .argument("", "Your message or question") - .option("-w, --wallet ", "Path to Solana wallet keypair") - .option("--json", "Output in JSON format") - .option("-q, --quiet", "Suppress progress output") - .option("--download ", "Download media to specified path") - .action(async (agentName: string, message: string, options) => { - try { - const config = loadConfig(); - const apiUrl = config.apiUrl!; - const rpcUrl = config.rpcUrl!; - const walletPath = - options.wallet || config.wallet || getDefaultWalletPath(); - - if (!walletPath) { - console.error( - formatError( - "No wallet specified. Use --wallet or set MEMEPUTER_WALLET", - ), - ); - process.exit(1); - } - - const quiet = options.quiet || options.json; - const agentId = agentName.toLowerCase(); - - // Load wallet - let spinner = quiet ? null : ora("Loading wallet...").start(); - const wallet = loadWallet(walletPath); - if (spinner) { - spinner.succeed( - `Wallet loaded: ${formatPublicKey(wallet.publicKey.toBase58())}`, - ); - } - - // Connect to Solana - spinner = quiet ? null : ora("Connecting to Solana...").start(); - const connection = new Connection(rpcUrl, "confirmed"); - if (spinner) { - spinner.succeed("Connected to Solana"); - } - - // Create SDK instance - const memeputer = new Memeputer({ - apiUrl, - rpcUrl, - wallet, - connection, - }); - - // Call agent with prompt - spinner = quiet - ? null - : ora( - `Prompting ${formatAgent(agentName)}... (processing payment via x402)`, - ).start(); - - const result = await memeputer.prompt({ - agentId, - message, - }); - - if (spinner) { - spinner.succeed(`Response received from ${formatAgent(agentName)}`); - } - - // Handle async operations - automatically wait if statusUrl is present - if (result.statusUrl) { - spinner = quiet - ? null - : ora("Waiting for operation to complete...").start(); - - const statusResult = await memeputer.pollStatus(result.statusUrl, { - intervalMs: 5000, - maxAttempts: 60, - onProgress: (attempt, status) => { - if (spinner && attempt > 1) { - const elapsed = attempt * 5; - spinner.text = `Waiting... (${elapsed}s, status: ${status.status})`; - } - }, - }); - - if (spinner) { - if (statusResult.status === "completed") { - spinner.succeed("Operation completed!"); - } else { - spinner.fail( - `Operation ${statusResult.status}: ${statusResult.error || "Unknown error"}`, - ); - } - } - - // Update result with completed data - if (statusResult.imageUrl) { - result.imageUrl = statusResult.imageUrl; - } - if (statusResult.mediaUrl) { - result.mediaUrl = statusResult.mediaUrl; - } - if (statusResult.message) { - result.response = statusResult.message; - } - - // Handle download if requested - if (options.download && (result.imageUrl || result.mediaUrl)) { - const downloadUrl = result.imageUrl || result.mediaUrl; - - // Generate unique filename with timestamp if downloading to avoid overwriting - const path = await import("path"); - const downloadPath = options.download; - const ext = path.extname(downloadPath); - const base = path.basename(downloadPath, ext); - const dir = path.dirname(downloadPath); - const timestamp = new Date() - .toISOString() - .replace(/[:.]/g, "-") - .slice(0, -5); - const uniquePath = path.join(dir, `${base}-${timestamp}${ext}`); - - spinner = quiet ? null : ora(`Downloading...`).start(); - - try { - const axios = (await import("axios")).default; - const fs = (await import("fs")).default; - const response = await axios.get(downloadUrl!, { - responseType: "arraybuffer", - }); - fs.writeFileSync(uniquePath, response.data); - - if (spinner) { - spinner.succeed(`Downloaded to ${uniquePath}`); - } - } catch (downloadError: any) { - if (spinner) { - spinner.fail(`Download failed: ${downloadError.message}`); - } - } - } - } - - // Output results - if (options.json) { - console.log( - JSON.stringify( - { - success: result.success, - response: result.response, - format: result.format, - mediaUrl: result.mediaUrl, - imageUrl: result.imageUrl, - statusUrl: result.statusUrl, - etaSeconds: result.etaSeconds, - signature: result.transactionSignature, - agent: agentName, - }, - null, - 2, - ), - ); - } else { - console.log(); - console.log(formatSuccess(`${agentName} says:`)); - console.log(); - console.log(chalk.white(result.response)); - console.log(); - - if (result.imageUrl) { - console.log( - formatInfo(`πŸ–ΌοΈ Image: ${chalk.underline(result.imageUrl)}`), - ); - } - - if (result.mediaUrl && !result.imageUrl) { - console.log( - formatInfo(`πŸ“Ž Media: ${chalk.underline(result.mediaUrl)}`), - ); - } - - if (result.transactionSignature) { - console.log( - formatInfo( - `πŸ’³ Transaction: ${chalk.underline(`https://solscan.io/tx/${result.transactionSignature}`)}`, - ), - ); - console.log( - chalk.green( - "πŸ’š You paid $0 in gas fees (PayAI Facilitator covered it)!", - ), - ); - } - console.log(); - } - } catch (error: any) { - console.error(formatError(error.message || "Failed to get response")); - process.exit(1); - } - }); -} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts deleted file mode 100644 index b427956..0000000 --- a/packages/cli/src/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env node - -import { Command } from "commander"; -import chalk from "chalk"; -import { createPromptCommand } from "./commands/prompt.js"; -import { createAgentsCommand } from "./commands/agents.js"; -import { createBalanceCommand } from "./commands/balance.js"; -import { commandCommand } from "./commands/command.js"; -import { readFileSync } from "fs"; -import { join, dirname } from "path"; -import { fileURLToPath } from "url"; - -// Read version from package.json -const __dirname = dirname(fileURLToPath(import.meta.url)); -const packageJson = JSON.parse( - readFileSync(join(__dirname, "../package.json"), "utf-8"), -); - -const program = new Command(); - -program - .name("memeputer") - .description( - chalk.bold("πŸ€– Memeputer CLI") + - "\n Pay and interact with AI agents via x402 micropayments", - ) - .version(packageJson.version); - -// Add commands -program.addCommand(createPromptCommand()); -program.addCommand(commandCommand); -program.addCommand(createAgentsCommand()); -program.addCommand(createBalanceCommand()); - -// Examples in help -program.on("--help", () => { - console.log(); - console.log("Examples:"); - console.log(' $ memeputer prompt FINNPUTER "whats up"'); - console.log(" $ memeputer command rawgroundbeef ping"); - console.log( - ' $ memeputer command rawgroundbeef pfp "a cool cyberpunk hacker"', - ); - console.log(" $ memeputer command trendputer get_trends --sources RSS --max-items 3"); - console.log(" $ memeputer command trendputer get_trends --sources RSS --max-items=3 --include-hashtags=true"); - console.log(" $ memeputer agents"); - console.log(" $ memeputer balance"); - console.log(); - console.log("Configuration:"); - console.log(" Create ~/.memeputerrc with your default settings"); - console.log(); - console.log("Learn more:"); - console.log(" Website: https://memeputer.com"); - console.log(" Marketplace: https://marketplace.memeputer.com"); - console.log(" API Docs: https://agents.memeputer.com/docs"); - console.log(); -}); - -// Parse and execute -program.parse(); diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts deleted file mode 100644 index 61c572e..0000000 --- a/packages/cli/src/lib/config.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { homedir } from "os"; -import { join } from "path"; -import { readFileSync, existsSync } from "fs"; - -export interface Config { - wallet?: string; - network?: string; - apiUrl?: string; - rpcUrl?: string; - chain?: string; // Blockchain: 'solana' (default) | 'base' -} - -const CONFIG_PATH = join(homedir(), ".memeputerrc"); - -export function loadConfig(): Config { - const config: Config = { - network: "mainnet-beta", - chain: "solana", // Default to Solana - apiUrl: - process.env.NODE_ENV === "development" - ? "http://localhost:3006/x402" - : "https://agents.memeputer.com/x402", // Default to production - }; - - // Load from config file - if (existsSync(CONFIG_PATH)) { - try { - const fileConfig = JSON.parse(readFileSync(CONFIG_PATH, "utf-8")); - Object.assign(config, fileConfig); - } catch (error) { - // Ignore config file errors - } - } - - // Override with environment variables - if (process.env.MEMEPUTER_WALLET) { - config.wallet = process.env.MEMEPUTER_WALLET; - } - if (process.env.MEMEPUTER_API_URL) { - config.apiUrl = process.env.MEMEPUTER_API_URL; - } - if (process.env.MEMEPUTER_CHAIN) { - config.chain = process.env.MEMEPUTER_CHAIN; - } - if (process.env.SOLANA_RPC_URL) { - config.rpcUrl = process.env.SOLANA_RPC_URL; - } - - // Set default RPC URL if not specified - if (!config.rpcUrl) { - // Use Helius for better reliability (requires HELIUS_API_KEY env var) - // Or fallback to public RPC endpoints - const heliusKey = process.env.HELIUS_API_KEY; - if (heliusKey) { - config.rpcUrl = - config.network === "mainnet-beta" - ? `https://rpc.helius.xyz/?api-key=${heliusKey}` - : "https://api.devnet.solana.com"; - } else { - // Fallback to public RPC endpoints - config.rpcUrl = - config.network === "mainnet-beta" - ? "https://api.mainnet-beta.solana.com" - : "https://api.devnet.solana.com"; - } - } - - return config; -} - -export function getDefaultWalletPath(): string { - return join(homedir(), ".config", "solana", "id.json"); -} diff --git a/packages/cli/src/lib/wallet.ts b/packages/cli/src/lib/wallet.ts deleted file mode 100644 index 998c60d..0000000 --- a/packages/cli/src/lib/wallet.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Keypair } from "@solana/web3.js"; -import { readFileSync, existsSync } from "fs"; -import bs58 from "bs58"; - -export function loadWallet(walletPath: string): Keypair { - if (!existsSync(walletPath)) { - throw new Error( - `Wallet file not found: ${walletPath}\n\n` + - `Create a wallet with: solana-keygen new --outfile ${walletPath}`, - ); - } - - try { - const walletData = readFileSync(walletPath, "utf-8"); - const secretKey = JSON.parse(walletData); - - // Handle both array format and base58 string format - if (Array.isArray(secretKey)) { - return Keypair.fromSecretKey(Uint8Array.from(secretKey)); - } else if (typeof secretKey === "string") { - return Keypair.fromSecretKey(bs58.decode(secretKey)); - } else { - throw new Error("Invalid wallet format"); - } - } catch (error: any) { - throw new Error(`Failed to load wallet: ${error.message}`); - } -} - -export function formatPublicKey(publicKey: string): string { - return `${publicKey.slice(0, 4)}...${publicKey.slice(-4)}`; -} diff --git a/packages/cli/src/utils/formatting.ts b/packages/cli/src/utils/formatting.ts deleted file mode 100644 index 0c96e3c..0000000 --- a/packages/cli/src/utils/formatting.ts +++ /dev/null @@ -1,100 +0,0 @@ -import chalk from "chalk"; - -export function formatSuccess(message: string): string { - return chalk.green("βœ… " + message); -} - -export function formatError(message: string): string { - return chalk.red("❌ " + message); -} - -export function formatInfo(message: string): string { - return chalk.blue("ℹ️ " + message); -} - -export function formatWarning(message: string): string { - return chalk.yellow("⚠️ " + message); -} - -export function formatPrice(usdc: number): string { - return chalk.cyan(`$${usdc.toFixed(2)} USDC`); -} - -export function formatAgent(name: string): string { - return chalk.magenta(`πŸ€– ${name}`); -} - -export function formatPublicKey(key: string): string { - if (key.length <= 12) return chalk.gray(key); - return chalk.gray(`${key.slice(0, 4)}...${key.slice(-4)}`); -} - -export function formatUsdc(amount: number): string { - let decimals = 2; - if (amount < 0.01 && amount > 0) { - decimals = 4; - } - return chalk.cyan(`${amount.toFixed(decimals)} USDC`); -} - -export function formatUrl(url: string): string { - return chalk.underline.blue(url); -} - -export function formatSignature(signature: string): string { - return chalk.gray(`${signature.slice(0, 8)}...${signature.slice(-8)}`); -} - -export function printBox(title: string, content: string[]): void { - const width = Math.max( - title.length, - ...content.map((line) => stripAnsi(line).length), - ); - const border = "─".repeat(width + 4); - - console.log(`β”Œ${border}┐`); - console.log( - `β”‚ ${chalk.bold(title)}${" ".repeat(width - title.length + 2)}β”‚`, - ); - console.log(`β”œ${border}─`); - - for (const line of content) { - const stripped = stripAnsi(line); - const padding = " ".repeat(width - stripped.length); - console.log(`β”‚ ${line}${padding} β”‚`); - } - - console.log(`β””${border}β”˜`); -} - -// Simple ANSI strip for box formatting -function stripAnsi(str: string): string { - return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, ""); -} - -export function formatTable(headers: string[], rows: string[][]): void { - // Calculate column widths - const widths = headers.map((header, i) => { - const rowWidths = rows.map((row) => stripAnsi(row[i] || "").length); - return Math.max(header.length, ...rowWidths); - }); - - // Print header - const headerRow = headers - .map((header, i) => header.padEnd(widths[i])) - .join(" "); - console.log(chalk.bold(headerRow)); - console.log("─".repeat(widths.reduce((a, b) => a + b + 2, 0))); - - // Print rows - for (const row of rows) { - const formattedRow = row - .map((cell, i) => { - const stripped = stripAnsi(cell); - const padding = widths[i] - stripped.length; - return cell + " ".repeat(padding); - }) - .join(" "); - console.log(formattedRow); - } -} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json deleted file mode 100644 index 08d89e0..0000000 --- a/packages/cli/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "node", - "lib": ["ES2022"], - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/sdk/.npmignore b/packages/sdk/.npmignore deleted file mode 100644 index 9ab0f52..0000000 --- a/packages/sdk/.npmignore +++ /dev/null @@ -1,35 +0,0 @@ -# Source files -src/ -*.ts -!*.d.ts - -# Test files -*.test.ts -*.test.js -*.test.d.ts -*.spec.ts -*.spec.js -*.spec.d.ts -vitest.config.ts -dist/*.test.* -dist/*.spec.* - -# Development files -tsconfig.json -.vscode/ -.idea/ - -# Build artifacts (keep dist/) -*.map - -# Dependencies -node_modules/ - -# Git -.git/ -.gitignore - -# Misc -.DS_Store -*.log - diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md deleted file mode 100644 index d9116bb..0000000 --- a/packages/sdk/CHANGELOG.md +++ /dev/null @@ -1,122 +0,0 @@ -# @memeputer/sdk - -## 1.9.1 - -### Patch Changes - -- Fix EIP-3009 authorization clock skew issue - - Add 60-second buffer to validAfter timestamp to account for blockchain clock skew - - Prevents "authorization is not yet valid" errors when blockchain timestamp is slightly behind -- Fix Solana fee payer address handling - - Use fee payer address from 402 response instead of hardcoded value - - Update fallback fee payer to correct address (561oabzy81vXYYbs1ZHR1bvpiEr6Nbfd6PGTxPshoz4p) - - Fixes transaction signature verification failures - -## 1.7.0 - -### Minor Changes - -- Improve error logging for debugging webhook issues - - Add detailed error logging for 500 errors with request payload details - - Log retry request payload to help diagnose webhook validation errors - - Add validation logging for array parameters (e.g., qualityModifiers) - - Improve error context in error messages - -- Enhance command-specific endpoint support - - Preserve command-specific endpoints in retry flow (fixes endpoint switching) - - Better handling of command-specific endpoints when backend returns base endpoint - - Add logging for endpoint preservation to aid debugging - -- Add more commands to jsonPayloadCommands list - - Add `keywords` and `select_best_trend` to jsonPayloadCommands for structured handling - -## 1.6.0 - -### Minor Changes - -- Add `discover_trends` command support - - Add `discover_trends` to `jsonPayloadCommands` for structured command handling - - Enables reliable JSON parsing for trendputer agent - -- Fix double `/x402` prefix in resource URL construction - - Handle absolute URLs with double prefix from backend - - Strip `/x402` prefix from relative paths when baseUrl already includes it - - Improve resource URL handling for x402 payment flow - -- Add URL construction debug logging - - Log base URL, chain, agent ID, and final request URL - - Log message preview for debugging - - Improve error messages with detailed URL information - -- Improve error handling - - Better error messages for 404 responses - - Handle empty response bodies gracefully - - More detailed error context for debugging - -## 1.5.0 - -### Minor Changes - -- Add Base/EVM wallet support with auto-detection - - Auto-detect Base wallets from `MEMEPUTER_BASE_WALLET_PRIVATE_KEY` env var, `~/.memeputerrc` config, or `~/.memeputer/base-wallet.json` - - Automatically switch between Solana and Base wallets based on agent payment requests - - Add `autoDetectBaseWallet()` utility function - - Export `BaseWallet` interface - -- Implement EIP-3009 authorization format for Base payments - - Switch from raw transaction format to EIP-3009 `signature` + `authorization` format - - Enables PayAI facilitator for gasless Base transactions - - Use EIP-712 typed data signing for authorizations - - Generate proper nonces for transfer authorizations - -- Add Base USDC balance checking - - Add `getBaseUsdcBalance()` function to check USDC balance on Base network - - Support both Solana and Base balance queries - -- Improve transaction hash handling - - Compute transaction hash client-side for Base payments - - Prefer backend-provided transaction signature when valid - - Fallback to computed hash for Base transactions - -- Fix payer address extraction - - Derive Base wallet address from private key when missing - - Prevent "unknown" payer addresses in receipts - -## 1.4.0 - -### Minor Changes - -- Add multi-chain support for Solana and Base (EVM) chains - - Add chain parameter to SDK and CLI with auto-detection from environment - - Update API URL structure to /x402/{chain}/{agentId} - - Implement EVM payment transaction creation using ethers.js - - Add wallet generation scripts for both Solana and Base - - Support MEMEPUTER_CHAIN environment variable - - Update all tests for multi-chain URL structure - - **Breaking Changes:** - - API endpoint structure changed from `/x402/interact` to `/x402/{chain}/{agentId}` - - `interact()` method signature updated to accept `Keypair | any` for multi-chain wallet support - - **Client-side ready:** - - βœ… Solana payments working in production - - βœ… Base payment creation implemented (pending backend support) - -## 1.3.0 - -### Minor Changes - -- Migrate to unified domain agents.memeputer.com/x402 - - BREAKING CHANGE: The default API URL has changed from `agents.api.memeputer.com` to `agents.memeputer.com/x402`. The endpoint structure has also changed - agent IDs are now in the URL path (e.g., `POST /x402/{agentId}`) instead of the request body. - - **For users:** - - Update your configuration files to use the new domain - - Environment variable `MEMEPUTER_API_URL` should be set to `https://agents.memeputer.com/x402` - - Local development now uses `http://localhost:3006/x402` - - **Changes:** - - Default API URLs updated across SDK and CLI - - Agent ID now in URL path instead of request body - - Endpoint paths simplified (no duplicate `/x402` segments) - - All tests updated to reflect new structure diff --git a/packages/sdk/README.md b/packages/sdk/README.md deleted file mode 100644 index 5c92d45..0000000 --- a/packages/sdk/README.md +++ /dev/null @@ -1,271 +0,0 @@ -# @memeputer/sdk - -SDK for interacting with Memeputer AI agents via x402 micropayments on Solana and Base. - -## Installation - -```bash -npm install @memeputer/sdk -# or -pnpm add @memeputer/sdk -# or -yarn add @memeputer/sdk -``` - -## Quick Start - -```typescript -import { Memeputer } from '@memeputer/sdk'; -import { Connection, Keypair } from '@solana/web3.js'; - -// Initialize SDK -const memeputer = new Memeputer({ - apiUrl: 'https://agents.memeputer.com/x402', - wallet: yourWallet, // Solana Keypair or EVM wallet - connection: yourConnection, // Solana Connection or EVM provider -}); - -// Prompt an agent -const result = await memeputer.prompt('memeputer', 'Hello!'); -console.log(result.response); -``` - -## Usage - -### Prompting Agents - -Send a natural language message to an agent: - -```typescript -// String overload -const result = await memeputer.prompt('memeputer', 'What is the weather?'); - -// Object syntax -const result = await memeputer.prompt({ - agentId: 'memeputer', - message: 'What is the weather?' -}); - -console.log(result.response); -``` - -### Executing Commands - -Execute structured commands on agents. The SDK automatically converts camelCase keys to kebab-case CLI flags. - -#### Simple Command (No Parameters) - -```typescript -const result = await memeputer.command('memeputer', 'ping'); -``` - -#### Command with Positional Arguments - -```typescript -const result = await memeputer.command('pfpputer', 'pfp', [ - 'generate', - 'a cat wearing sunglasses' -]); -``` - -#### Command with Named Parameters (camelCase β†’ kebab-case) - -Use camelCase keys in your code - the SDK automatically converts them to kebab-case CLI flags: - -```typescript -// camelCase keys automatically become --kebab-case flags -const result = await memeputer.command('pfpputer', 'pfp', { - refImages: ['url1', 'url2'], // β†’ --ref-images url1 url2 - maxWidth: 1024, // β†’ --max-width 1024 - style: 'anime' // β†’ --style anime -}); -``` - -#### Command with Positional Args + Flags - -Use the `_args` key for positional arguments, and camelCase keys for flags: - -```typescript -const result = await memeputer.command('pfpputer', 'pfp', { - _args: ['generate', 'a cat'], // Positional arguments - refImages: ['url1', 'url2'], // β†’ --ref-images url1 url2 - maxWidth: 1024 // β†’ --max-width 1024 -}); -``` - -#### Object Syntax - -```typescript -const result = await memeputer.command({ - agentId: 'pfpputer', - command: 'pfp', - params: { - _args: ['generate', 'a cat'], - refImages: ['url1', 'url2'] - } -}); -``` - -## camelCase to kebab-case Conversion - -The SDK automatically converts camelCase object keys to kebab-case CLI flags: - -| camelCase (in code) | CLI Flag | -|---------------------|----------| -| `refImages` | `--ref-images` | -| `maxWidth` | `--max-width` | -| `multiWordKeyName` | `--multi-word-key-name` | - -**Example:** - -```typescript -// Write this: -await memeputer.command('agent', 'cmd', { - refImages: ['url1', 'url2'], - maxWidth: 1024 -}); - -// SDK converts to: -// /cmd --ref-images url1 url2 --max-width 1024 -``` - -## Positional Arguments - -Use the `_args` or `args` key to specify positional arguments: - -```typescript -// Using _args -await memeputer.command('agent', 'cmd', { - _args: ['subcommand', 'value'], - flag: 'value' -}); - -// Using args (alternative) -await memeputer.command('agent', 'cmd', { - args: ['subcommand', 'value'], - flag: 'value' -}); -``` - -## Complex Parameters (JSON Format) - -Some commands require complex objects and are automatically sent as JSON: - -```typescript -// Commands like generate_brief, describe_image, etc. use JSON format -const result = await memeputer.command('briefputer', 'generate_brief', { - trendItem: { title: 'Test', summary: 'Test summary' }, - policy: { denyTerms: [] } -}); -``` - -## Configuration - -```typescript -import { Memeputer } from '@memeputer/sdk'; -import { Connection, Keypair } from '@solana/web3.js'; - -const memeputer = new Memeputer({ - apiUrl: 'https://agents.memeputer.com/x402', // Optional, defaults to production - chain: 'solana', // 'solana' (default) or 'base' - wallet: yourWallet, // Solana Keypair or EVM wallet - connection: yourConnection, // Solana Connection or EVM provider - verbose: true, // Enable verbose x402 logging -}); -``` - -### Auto-detection - -If you don't provide wallet/connection, the SDK will auto-detect: - -- **Solana**: Uses `~/.config/solana/id.json` if available -- **Base**: Uses `~/.memeputer/base-wallet.json` if available -- **RPC**: Uses environment variables or defaults - -## Response Format - -Both `prompt()` and `command()` return an `InteractionResult`: - -```typescript -interface InteractionResult { - response: string; // Agent's response text - success: boolean; // Whether the interaction succeeded - format: 'text' | 'json'; // Response format - agentId: string; // Agent ID that responded - transactionSignature?: string; // Payment transaction signature (if payment occurred) - x402Receipt?: { // x402 payment receipt - amountPaidUsdc: number; - payer: string; - merchant: string; - // ... more fields - }; - imageUrl?: string; // Image URL (if applicable) - mediaUrl?: string; // Media URL (if applicable) - statusUrl?: string; // Status URL for async operations -} -``` - -## Async Operations - -Some operations are asynchronous and return a `statusUrl`: - -```typescript -const result = await memeputer.command('pfpputer', 'pfp', { - _args: ['generate', 'a cat'] -}); - -if (result.statusUrl) { - // Poll for completion - const status = await memeputer.pollStatus(result.statusUrl, { - maxAttempts: 120, - intervalMs: 1000, - onProgress: (attempt, status) => { - console.log(`Attempt ${attempt}: ${status.status}`); - } - }); - - console.log('Final result:', status.imageUrl); -} -``` - -## Examples - -### Generate an Image - -```typescript -const result = await memeputer.command('pfpputer', 'pfp', { - _args: ['generate', 'a cat wearing sunglasses'], - refImages: ['https://example.com/reference.jpg'], - maxWidth: 1024 -}); - -console.log('Image URL:', result.imageUrl); -``` - -### Discover Trends - -```typescript -const result = await memeputer.command('trendputer', 'discover_trends', { - keywords: ['crypto', 'solana'], - maxResults: 10, - includeHashtags: true -}); - -const trends = JSON.parse(result.response); -console.log('Trends:', trends.items); -``` - -## TypeScript Support - -Full TypeScript support with type definitions included: - -```typescript -import { Memeputer, PromptResult, CommandResult } from '@memeputer/sdk'; - -const result: CommandResult = await memeputer.command('agent', 'cmd'); -``` - -## License - -MIT - diff --git a/packages/sdk/package.json b/packages/sdk/package.json deleted file mode 100644 index dbb83bb..0000000 --- a/packages/sdk/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "@memeputer/sdk", - "version": "1.9.1", - "description": "SDK for interacting with Memeputer AI agents via x402 payments", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "type": "module", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "vitest", - "test:ui": "vitest --ui", - "test:run": "vitest run", - "prepublishOnly": "npm run build" - }, - "keywords": [ - "memeputer", - "ai", - "agents", - "sdk", - "x402", - "solana", - "micropayments" - ], - "author": "Memeputer", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/memeputer/memeputer-oss.git", - "directory": "packages/sdk" - }, - "homepage": "https://memeputer.com", - "bugs": { - "url": "https://github.com/memeputer/memeputer-oss/issues" - }, - "dependencies": { - "@solana/spl-token": "^0.4.14", - "@solana/web3.js": "^1.98.4", - "axios": "^1.6.5", - "bs58": "^5.0.0", - "ethers": "^6.13.0", - "x402-solana": "^0.1.4" - }, - "devDependencies": { - "@types/node": "^20.10.6", - "@vitest/ui": "^4.0.8", - "typescript": "^5.3.3", - "vitest": "^4.0.8" - }, - "engines": { - "node": ">=18.0.0" - } -} diff --git a/packages/sdk/src/__tests__/api.test.ts b/packages/sdk/src/__tests__/api.test.ts deleted file mode 100644 index 336de84..0000000 --- a/packages/sdk/src/__tests__/api.test.ts +++ /dev/null @@ -1,396 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { AgentsApiClient } from '../api'; -import axios from 'axios'; -import { Connection, Keypair } from '@solana/web3.js'; - -// Mock axios -vi.mock('axios'); - -// Mock the x402Client module -vi.mock('../x402Client', () => ({ - createPaymentTransaction: vi.fn().mockResolvedValue({ - transaction: {}, - signature: 'mock-payment-header-base64', - }), - getUsdcBalance: vi.fn().mockResolvedValue(10.0), -})); - -describe('AgentsApiClient - x402 Protocol', () => { - let client: AgentsApiClient; - let mockWallet: Keypair; - let mockConnection: Connection; - - beforeEach(() => { - client = new AgentsApiClient('https://agents.memeputer.com/x402'); - mockWallet = Keypair.generate(); - mockConnection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed'); - vi.clearAllMocks(); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - describe('Atomic Units Parsing (x402 Spec)', () => { - it('should parse atomic units correctly for 0.01 USDC (10000 micro-USDC)', async () => { - // Mock 402 response with atomic units per x402 spec - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - network: 'solana-mainnet', - maxAmountRequired: '10000', // Atomic units (micro-USDC) per x402 spec - resource: 'https://agents.memeputer.com/x402/interact', - payTo: 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ', - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - // Mock successful payment response - const mockSuccessResponse = { - status: 200, - data: { - x402Version: 1, - success: true, - response: 'Payment processed', - agentId: 'test-agent', - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response) - .mockResolvedValueOnce(mockSuccessResponse); - - const result = await client.interact('test-agent', 'test message', mockWallet, mockConnection); - - expect(result.success).toBe(true); - expect(result.response).toBe('Payment processed'); - }); - - it('should parse atomic units correctly for 0.10 USDC (100000 micro-USDC)', async () => { - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - network: 'solana-mainnet', - maxAmountRequired: '100000', // Atomic units (micro-USDC) per x402 spec - resource: 'https://agents.memeputer.com/x402/interact', - payTo: 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ', - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - const mockSuccessResponse = { - status: 200, - data: { - x402Version: 1, - success: true, - response: 'Payment processed', - x402Receipt: { - amountPaidUsdc: 0.1, - amountPaidMicroUsdc: 100000, - }, - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response) - .mockResolvedValueOnce(mockSuccessResponse); - - const result = await client.interact('test-agent', 'test message', mockWallet, mockConnection); - - expect(result.success).toBe(true); - expect(result.x402Receipt?.amountPaidUsdc).toBe(0.1); - expect(result.x402Receipt?.amountPaidMicroUsdc).toBe(100000); - }); - - it('should parse atomic units correctly for 1.0 USDC (1000000 micro-USDC)', async () => { - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - network: 'solana-mainnet', - maxAmountRequired: '1000000', // Atomic units (micro-USDC) per x402 spec - resource: 'https://agents.memeputer.com/x402/interact', - payTo: 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ', - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - const mockSuccessResponse = { - status: 200, - data: { - x402Version: 1, - success: true, - response: 'Payment processed', - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response) - .mockResolvedValueOnce(mockSuccessResponse); - - const result = await client.interact('test-agent', 'test message', mockWallet, mockConnection); - - expect(result.success).toBe(true); - }); - - it('should handle edge case: 0.5 USDC (500000 micro-USDC)', async () => { - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - network: 'solana-mainnet', - maxAmountRequired: '500000', // Atomic units (micro-USDC) per x402 spec - resource: 'https://agents.memeputer.com/x402/interact', - payTo: 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ', - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - const mockSuccessResponse = { - status: 200, - data: { - x402Version: 1, - success: true, - response: 'Payment processed', - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response) - .mockResolvedValueOnce(mockSuccessResponse); - - const result = await client.interact('test-agent', 'test message', mockWallet, mockConnection); - - expect(result.success).toBe(true); - }); - - it('should use default value if maxAmountRequired is missing', async () => { - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - network: 'solana-mainnet', - // maxAmountRequired missing - should default to "10000" (0.01 USDC) - resource: 'https://agents.memeputer.com/x402/interact', - payTo: 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ', - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - const mockSuccessResponse = { - status: 200, - data: { - x402Version: 1, - success: true, - response: 'Payment processed', - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response) - .mockResolvedValueOnce(mockSuccessResponse); - - const result = await client.interact('test-agent', 'test message', mockWallet, mockConnection); - - expect(result.success).toBe(true); - }); - }); - - describe('x402 Resource URL', () => { - it('should use resource URL from 402 response for paid request', async () => { - const customResourceUrl = 'https://custom-endpoint.com/x402/interact'; - - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - network: 'solana-mainnet', - maxAmountRequired: '0.01', - resource: customResourceUrl, // Custom resource URL - payTo: 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ', - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - const mockSuccessResponse = { - status: 200, - data: { - x402Version: 1, - success: true, - response: 'Payment processed', - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response) - .mockResolvedValueOnce(mockSuccessResponse); - - await client.interact('test-agent', 'test message', mockWallet, mockConnection); - - // Verify the second call (paid request) used the custom resource URL - expect(axios.post).toHaveBeenCalledTimes(2); - const secondCallArgs = (axios.post as any).mock.calls[1]; - expect(secondCallArgs[0]).toBe(customResourceUrl); - }); - - it('should fallback to default endpoint if resource URL is missing', async () => { - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - network: 'solana-mainnet', - maxAmountRequired: '0.01', - // resource field missing - payTo: 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ', - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - const mockSuccessResponse = { - status: 200, - data: { - x402Version: 1, - success: true, - response: 'Payment processed', - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response) - .mockResolvedValueOnce(mockSuccessResponse); - - await client.interact('test-agent', 'test message', mockWallet, mockConnection); - - // Verify the second call used the default endpoint with chain and agentId in path - expect(axios.post).toHaveBeenCalledTimes(2); - const secondCallArgs = (axios.post as any).mock.calls[1]; - expect(secondCallArgs[0]).toBe('https://agents.memeputer.com/x402/solana/test-agent'); - }); - }); - - describe('Network Identifier', () => { - it('should use "solana-mainnet" as network identifier per x402 spec', async () => { - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - network: 'solana-mainnet', // Official x402 spec uses "{blockchain}-mainnet" format - maxAmountRequired: '10000', - resource: 'https://agents.memeputer.com/x402/interact', - payTo: 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ', - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - const mockSuccessResponse = { - status: 200, - data: { - x402Version: 1, - success: true, - response: 'Payment processed', - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response) - .mockResolvedValueOnce(mockSuccessResponse); - - const result = await client.interact('test-agent', 'test message', mockWallet, mockConnection); - - expect(result.success).toBe(true); - }); - - it('should default to "solana-mainnet" if network is missing', async () => { - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - // network missing - should default to "solana-mainnet" - maxAmountRequired: '0.01', - resource: 'https://agents.memeputer.com/x402/interact', - payTo: 'G31J8ZeVKo6j6xkxkjCcHENhQGNQid575MRvyixxNUJQ', - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - const mockSuccessResponse = { - status: 200, - data: { - x402Version: 1, - success: true, - response: 'Payment processed', - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response) - .mockResolvedValueOnce(mockSuccessResponse); - - const result = await client.interact('test-agent', 'test message', mockWallet, mockConnection); - - expect(result.success).toBe(true); - }); - }); - - describe('Error Handling', () => { - it('should throw error if accepts array is missing', async () => { - const mock402Response = { - status: 402, - data: { - x402Version: 1, - // accepts array missing - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response); - - await expect( - client.interact('test-agent', 'test message', mockWallet, mockConnection) - ).rejects.toThrow('No payment details in 402 response'); - }); - - it('should throw error if payTo is missing', async () => { - const mock402Response = { - status: 402, - data: { - x402Version: 1, - accepts: [{ - scheme: 'exact', - network: 'solana-mainnet', - maxAmountRequired: '0.01', - resource: 'https://agents.memeputer.com/x402/interact', - // payTo missing - asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - }], - }, - }; - - (axios.post as any).mockResolvedValueOnce(mock402Response); - - await expect( - client.interact('test-agent', 'test message', mockWallet, mockConnection) - ).rejects.toThrow('No recipient wallet (payTo) found in 402 response'); - }); - }); -}); - diff --git a/packages/sdk/src/__tests__/index.test.ts b/packages/sdk/src/__tests__/index.test.ts deleted file mode 100644 index 61ca4f4..0000000 --- a/packages/sdk/src/__tests__/index.test.ts +++ /dev/null @@ -1,374 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { Memeputer } from '../index'; -import { Connection, Keypair } from '@solana/web3.js'; - -// Mock the API client -vi.mock('../api', () => { - class MockAgentsApiClient { - interact = vi.fn(); - checkStatus = vi.fn(); - pollStatus = vi.fn(); - listAgents = vi.fn(); - } - return { - AgentsApiClient: MockAgentsApiClient, - }; -}); - -describe('Memeputer SDK', () => { - let memeputer: Memeputer; - let mockWallet: Keypair; - let mockConnection: Connection; - let mockApiClient: any; - - beforeEach(() => { - // Create a mock wallet and connection - mockWallet = Keypair.generate(); - mockConnection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed'); - - // Create SDK instance - memeputer = new Memeputer({ - apiUrl: 'https://agents.memeputer.com/x402', - rpcUrl: 'https://api.mainnet-beta.solana.com', - wallet: mockWallet, - connection: mockConnection, - }); - - // Get the mocked API client instance - mockApiClient = (memeputer as any).apiClient; - }); - - describe('prompt()', () => { - it('should call interact with string overload', async () => { - const mockResponse = { - response: 'Hello, world!', - success: true, - format: 'text', - agentId: 'test-agent', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.prompt('test-agent', 'Hello'); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'test-agent', - 'Hello', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('Hello, world!'); - }); - - it('should call interact with object overload', async () => { - const mockResponse = { - response: 'Response text', - success: true, - format: 'text', - agentId: 'test-agent', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.prompt({ - agentId: 'test-agent', - message: 'Test message', - }); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'test-agent', - 'Test message', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('Response text'); - }); - }); - - describe('command()', () => { - it('should call interact with simple command (no params)', async () => { - const mockResponse = { - response: 'pong', - success: true, - format: 'text', - agentId: 'memeputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('memeputer', 'ping'); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'memeputer', - '/ping', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('pong'); - }); - - it('should format command with positional params as CLI format', async () => { - const mockResponse = { - response: 'Success', - success: true, - format: 'text', - agentId: 'pfpputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('pfpputer', 'pfp', ['generate', 'a cat']); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'pfpputer', - '/pfp generate a cat', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('Success'); - }); - - it('should format command with named string params as CLI format', async () => { - const mockResponse = { - response: 'Success', - success: true, - format: 'text', - agentId: 'pfpputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('pfpputer', 'pfp', { - style: 'anime', - subject: 'cat', - }); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'pfpputer', - '/pfp --style anime --subject cat', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('Success'); - }); - - it('should send complex params as JSON', async () => { - const mockResponse = { - response: 'Brief generated', - success: true, - format: 'text', - agentId: 'briefputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('briefputer', 'generate_brief', { - trendItem: { title: 'Test', summary: 'Test summary' }, - policy: { denyTerms: [] }, - }); - - // Should use command-specific endpoint (empty message, params passed separately) - const callArgs = mockApiClient.interact.mock.calls[0]; - const message = callArgs[1]; - const command = callArgs[4]; - const params = callArgs[5]; - - expect(message).toBe(''); // Empty message for command-specific endpoint - expect(command).toBe('generate_brief'); - expect(params).toBeDefined(); - expect(params.trendItem).toBeDefined(); - expect(params.policy).toBeDefined(); - expect(result.response).toBe('Brief generated'); - }); - - it('should send commands in jsonPayloadCommands list as JSON even with simple params', async () => { - const mockResponse = { - response: 'Image described', - success: true, - format: 'text', - agentId: 'imagedescripterputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('imagedescripterputer', 'describe_image', { - imageUrl: 'https://example.com/image.png', - detailLevel: 'detailed', - }); - - // Should use command-specific endpoint because describe_image is in jsonPayloadCommands - const callArgs = mockApiClient.interact.mock.calls[0]; - const message = callArgs[1]; - const command = callArgs[4]; - const params = callArgs[5]; - - expect(message).toBe(''); // Empty message for command-specific endpoint - expect(command).toBe('describe_image'); - expect(params).toBeDefined(); - expect(params.imageUrl).toBe('https://example.com/image.png'); - expect(params.detailLevel).toBe('detailed'); - expect(result.response).toBe('Image described'); - }); - - it('should call interact with object syntax', async () => { - const mockResponse = { - response: 'pong', - success: true, - format: 'text', - agentId: 'memeputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command({ - agentId: 'memeputer', - command: 'ping', - }); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'memeputer', - '/ping', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('pong'); - }); - - describe('camelCase to kebab-case conversion', () => { - it('should convert camelCase keys to kebab-case flags', async () => { - const mockResponse = { - response: 'Success', - success: true, - format: 'text', - agentId: 'pfpputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('pfpputer', 'pfp', { - refImages: ['url1', 'url2'], - maxWidth: 1024, - }); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'pfpputer', - '/pfp --ref-images url1 url2 --max-width 1024', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('Success'); - }); - - it('should handle positional args with _args key', async () => { - const mockResponse = { - response: 'Success', - success: true, - format: 'text', - agentId: 'pfpputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('pfpputer', 'pfp', { - _args: ['generate', 'a cat'], - refImages: ['url1', 'url2'], - }); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'pfpputer', - '/pfp generate a cat --ref-images url1 url2', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('Success'); - }); - - it('should handle positional args with args key (alternative)', async () => { - const mockResponse = { - response: 'Success', - success: true, - format: 'text', - agentId: 'pfpputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('pfpputer', 'pfp', { - args: ['generate', 'a cat'], - refImages: ['url1'], - }); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'pfpputer', - '/pfp generate a cat --ref-images url1', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('Success'); - }); - - it('should handle single value flags (not arrays)', async () => { - const mockResponse = { - response: 'Success', - success: true, - format: 'text', - agentId: 'pfpputer', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('pfpputer', 'pfp', { - style: 'anime', - maxWidth: 1024, - }); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'pfpputer', - '/pfp --style anime --max-width 1024', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('Success'); - }); - - it('should handle complex camelCase conversions', async () => { - const mockResponse = { - response: 'Success', - success: true, - format: 'text', - agentId: 'test-agent', - transactionSignature: 'test-tx', - }; - - mockApiClient.interact.mockResolvedValue(mockResponse); - - const result = await memeputer.command('test-agent', 'test', { - camelCaseKey: 'value', - multiWordKeyName: 'value2', - single: 'value3', - }); - - expect(mockApiClient.interact).toHaveBeenCalledWith( - 'test-agent', - '/test --camel-case-key value --multi-word-key-name value2 --single value3', - mockWallet, - mockConnection, - ); - expect(result.response).toBe('Success'); - }); - }); - }); -}); - diff --git a/packages/sdk/src/api.ts b/packages/sdk/src/api.ts deleted file mode 100644 index 347c82a..0000000 --- a/packages/sdk/src/api.ts +++ /dev/null @@ -1,977 +0,0 @@ -import axios from "axios"; -import { Connection, Keypair } from "@solana/web3.js"; -import { createPaymentTransaction } from "./x402Client"; -import { autoDetectBaseWallet, BaseWallet } from "./utils"; - -export interface AgentInfo { - id: string; - name: string; - description: string; - price: number; - category: string; - examplePrompts: string[]; - payTo: string; -} - -export interface X402Receipt { - amountPaidUsdc: number; - amountPaidMicroUsdc: number; - payTo: string; - transactionSignature: string; - payer: string; - merchant: string; - timestamp: string; -} - -export interface InteractionResult { - success: boolean; - response: string; - format: "text" | "json" | "markdown"; // Standardized format field - mediaUrl?: string; - statusUrl?: string; // For async operations (HTTP 202) - imageUrl?: string; // Async image result - etaSeconds?: number; // Estimated time for async operations - retryAfterSeconds?: number; // Polling interval in seconds (default: 2) - transactionSignature?: string; - agentId?: string; // Logical agent name (e.g., "pfpputer", "trendputer") - agentUuid?: string; // Internal UUID (optional, for debugging/tracking) - timestamp?: string; // ISO 8601 timestamp - server response time - x402Version?: number; // Always 1 for standardized responses - error?: string; - code?: string; // Machine-readable error code - receipt?: X402Receipt; // Standardized receipt field (preferred) - x402Receipt?: X402Receipt; // Legacy field name (for backward compatibility) - x402Quote?: { - amountQuotedUsdc: number; // Amount quoted in 402 response (what we paid) - amountQuotedMicroUsdc: number; - maxAmountRequired: number; - }; // Quote details from 402 response - jobId?: string; // For async operations (HTTP 202) -} - -export interface StatusCheckResult { - status: "pending" | "processing" | "completed" | "failed"; // Legacy field - state?: "processing" | "succeeded" | "failed"; // Standardized state field - message?: string; - response?: string; // Final result content - artifactUrl?: string; // URL to final artifact - imageUrl?: string; - mediaUrl?: string; - error?: string; - code?: string; // Machine-readable error code (e.g., "generation_timeout", "generation_failed") -} - -export class AgentsApiClient { - private verbose: boolean = false; - private chain: string; - - constructor(private baseUrl: string, chain: string = 'solana') { - this.chain = chain; - } - - /** - * Enable verbose logging to show x402 protocol details - */ - enableVerbose() { - this.verbose = true; - } - - /** - * Disable verbose logging - */ - disableVerbose() { - this.verbose = false; - } - - /** - * List all available agents from the x402 resources endpoint - */ - async listAgents(): Promise { - const response = await axios.get(`${this.baseUrl}/${this.chain}/resources`); - const data = response.data; - - // Parse new x402 format: { x402Version: 1, accepts: [...] } - const acceptsList = data.accepts || []; - - const agents: AgentInfo[] = acceptsList.map((accept: any) => ({ - id: accept.extra?.agentId || accept.agentId || "unknown", - name: accept.extra?.agentName || accept.name || "Unknown Agent", - description: accept.description || "AI agent", - price: - accept.extra?.pricing?.amount || - parseFloat(accept.maxAmountRequired || "10000") / 1_000_000, - category: accept.extra?.category || "General AI", - examplePrompts: accept.extra?.examplePrompts || [], - payTo: accept.payTo || "", - })); - - return agents; - } - - /** - * Interact with an agent using x402 payment - * Manual x402 flow: 402 β†’ create payment β†’ retry with X-PAYMENT header - * - * @param agentId - The agent ID to interact with - * @param message - The message or command to send. Can be: - * - Natural language prompt (e.g., "Hello, how are you?") - * - CLI format command (e.g., "/ping" or "/ping --arg value") - * - JSON string with command (e.g., '{"command":"ping"}') - * @param wallet - Wallet for payment (Solana Keypair or EVM wallet) - * @param connection - Connection/provider (Solana Connection or EVM provider) - * @param command - Optional: Command name for command-specific endpoint (e.g., "discover_trends") - * @param params - Optional: Structured parameters for command-specific endpoint - */ - async interact( - agentId: string, - message: string, - wallet: Keypair | any, // Support both Solana Keypair and EVM wallets - connection: Connection | any, // Support both Solana Connection and EVM providers - command?: string, // Optional: Command name for command-specific endpoint - params?: Record, // Optional: Structured parameters for command-specific endpoint - ): Promise { - // Determine endpoint type early so it's accessible in error handler - const useCommandEndpoint = command && params && Object.keys(params).length > 0; - - try { - // Variables to track payment quote and details - let amountUsdc: number | undefined; - let amountMicroUsdc: number | undefined; - let paymentHeader: string | undefined; - let recipient: string | undefined; - let acceptDetails: any | undefined; - let paymentWallet: any = wallet; // Will be set based on network from 402 response - let computedTxHash: string | undefined; // Computed transaction hash for Base transactions - let normalizedNetwork: string = 'solana'; // Network from 402 response - - // Step 1: Make request without payment (will get 402) - // Construct URL: Use command-specific endpoint if command and params provided - // Base endpoint: baseUrl/chain/agentId (e.g., /x402/solana/trendputer) - // Command endpoint: baseUrl/chain/agentId/command (e.g., /x402/solana/trendputer/discover_trends) - let requestUrl: string; - - if (useCommandEndpoint) { - // Use command-specific endpoint for structured commands - requestUrl = `${this.baseUrl}/${this.chain}/${agentId}/${command}`; - } else { - // Use base endpoint for chat/CLI format - requestUrl = `${this.baseUrl}/${this.chain}/${agentId}`; - } - - // Build request body based on endpoint type - let requestBody: Record = {}; - - if (useCommandEndpoint) { - // Command-specific endpoint: Don't include 'command' field, only params - requestBody = { ...params }; - // Include message if provided (for backward compatibility) - if (message && message.trim() !== '') { - requestBody.message = message; - } - } else { - // Base endpoint: Use existing logic for chat/CLI format - // Detect if message is a command without parameters - // Backend expects: { command: "ping" } for commands without params - // Backend expects: { message: "..." } for prompts or commands with params - - // Handle empty string - treat as no message - if (!message || message.trim() === '') { - requestBody = {}; // Send empty body (backend should handle this) - } else { - const isCliCommand = message.startsWith('/'); - let commandName: string | undefined; - - // Try to parse as JSON to check if it's a JSON command - try { - const parsed = JSON.parse(message); - if (parsed.command) { - commandName = parsed.command; - // If JSON command has no params (only command field), send as { command: "ping" } - const hasParams = Object.keys(parsed).filter(k => k !== 'command').length > 0; - if (!hasParams) { - requestBody = { command: commandName }; - } else { - // JSON command with params - send as message (backend will parse it) - requestBody = { message }; - } - } else { - // JSON but not a command - send as message - requestBody = { message }; - } - } catch { - // Not JSON - check if it's a CLI command - if (isCliCommand) { - // Extract command name (e.g., "/ping" -> "ping", "/ping arg" -> "ping") - commandName = message.substring(1).trim().split(/\s+/)[0]; - // If CLI command has no params (just "/ping"), send as { command: "ping" } - const parts = message.substring(1).trim().split(/\s+/); - const hasParams = parts.length > 1; - if (!hasParams && commandName) { - requestBody = { command: commandName }; - } else { - // CLI command with params - send as message - requestBody = { message }; - } - } else { - // Natural language prompt - send as message - requestBody = { message }; - } - } - } - } - - // Always log URL construction and request body for debugging - console.log(`\n πŸ” URL Construction Debug:`); - console.log(` πŸ“‹ Base URL: ${this.baseUrl}`); - console.log(` πŸ”— Chain: ${this.chain}`); - console.log(` πŸ‘€ Agent ID: ${agentId}`); - if (useCommandEndpoint) { - console.log(` 🎯 Command: ${command}`); - console.log(` πŸ”— Endpoint Type: Command-specific`); - console.log(` πŸ”— Final Request URL: ${requestUrl}`); - } else { - console.log(` πŸ”— Endpoint Type: Base (chat)`); - console.log(` πŸ”— Final Request URL: ${requestUrl}`); - } - console.log(`\n πŸ“¦ Request Payload:`); - console.log(` ${JSON.stringify(requestBody, null, 2).split('\n').join('\n ')}`); - if (useCommandEndpoint) { - console.log(` βœ… Using command-specific endpoint (no 'command' field in body)`); - } else if (requestBody.command) { - console.log(` βœ… Using 'command' field (no 'message' field)`); - } else if (requestBody.message) { - console.log(` βœ… Using 'message' field`); - if (requestBody.message.length > 200) { - console.log(` πŸ“ Message preview: ${requestBody.message.substring(0, 200)}...`); - } - } else { - console.log(` ⚠️ Empty request body (no 'message' or 'command' field)`); - } - console.log(''); - - let response; - try { - response = await axios.post( - requestUrl, - requestBody, - { - headers: { - "Content-Type": "application/json", - "User-Agent": "@memeputer/sdk", - }, - validateStatus: (status) => status < 500, // Don't throw on 402, but throw on 5xx - }, - ); - - // Log response status - if (this.verbose) { - console.log(` πŸ“‘ HTTP Response Status: ${response.status}`); - console.log(` πŸ“¦ Request Payload Sent: ${JSON.stringify(requestBody)}`); - if (response.status === 200) { - console.log(` ⚠️ WARNING: Got 200 response instead of 402. Backend may not be following x402 spec.`); - console.log(` πŸ’‘ Expected: 402 Payment Required β†’ Create Payment β†’ Retry with X-PAYMENT header β†’ 200 OK`); - console.log(` πŸ” Actual: 200 OK (payment may have been processed automatically)`); - } - if (response.status === 404) { - console.log(` ❌ ERROR: 404 Not Found`); - console.log(` πŸ’‘ Check: Agent ID "${agentId}" might not exist or endpoint structure is incorrect`); - console.log(` πŸ’‘ Expected endpoint: ${this.baseUrl}/${this.chain}/${agentId}`); - if (response.data) { - console.log(` πŸ“„ Response body: ${JSON.stringify(response.data).substring(0, 200)}`); - } - } - } - } catch (error: any) { - if (error.response?.status === 402) { - // Got 402 - payment required - response = error.response; - } else { - throw error; - } - } - - // Check if we got a 402 response - if (response.status === 402) { - // Step 2: Get QUOTE from 402 response (before payment) - // This is the estimated cost - use as budget limit - const paymentReq = response.data; - - // x402 format has payment details in accepts array - acceptDetails = paymentReq.accepts?.[0]; - - if (!acceptDetails) { - throw new Error( - `No payment details in 402 response. Response: ${JSON.stringify(paymentReq)}`, - ); - } - - recipient = acceptDetails.payTo; - // Per x402 spec: maxAmountRequired should be in atomic units (micro-USDC) - // However, backend may send decimal format (e.g., "0.03") or atomic units (e.g., "30000") - // We need to detect and handle both formats - const maxAmountRequired = acceptDetails.maxAmountRequired; - let atomicUnits: number; - - if (maxAmountRequired === undefined || maxAmountRequired === null) { - // Default to 0.01 USDC if missing - atomicUnits = 10000; - } else if (typeof maxAmountRequired === 'number') { - // If number is < 1, assume it's decimal USDC (e.g., 0.03) - // If number is >= 1, assume it's atomic units (e.g., 30000) - if (maxAmountRequired < 1) { - atomicUnits = Math.floor(maxAmountRequired * 1_000_000); - } else { - atomicUnits = Math.floor(maxAmountRequired); - } - } else if (typeof maxAmountRequired === 'string') { - // Check if string contains a decimal point (decimal format) - if (maxAmountRequired.includes('.')) { - // Decimal format (e.g., "0.03") - parse as float and convert to atomic units - const decimalUsdc = parseFloat(maxAmountRequired); - if (isNaN(decimalUsdc)) { - atomicUnits = 10000; // Fallback - } else { - atomicUnits = Math.floor(decimalUsdc * 1_000_000); - } - } else { - // Integer format (e.g., "30000") - parse as atomic units directly - atomicUnits = parseInt(maxAmountRequired, 10); - if (isNaN(atomicUnits)) { - atomicUnits = 10000; // Fallback - } - } - } else { - // Fallback for any other type - atomicUnits = 10000; - } - - amountUsdc = atomicUnits / 1_000_000; // Convert to USDC (6 decimals) for display - amountMicroUsdc = atomicUnits; // Keep atomic units for payment - const feePayer = acceptDetails.extra?.feePayer; - const scheme = acceptDetails.scheme || "exact"; - // IMPORTANT: Use network from 402 response, not this.chain - // The agent requests a specific network (e.g., "base"), not the client's default chain - const network = acceptDetails.network || "solana-mainnet"; - - // Normalize network name (handle variations like "base", "base-mainnet", "solana", "solana-mainnet") - normalizedNetwork = network.toLowerCase().includes('base') || network.toLowerCase().includes('ethereum') || network.toLowerCase().includes('polygon') || network.toLowerCase().includes('arbitrum') - ? (network.toLowerCase().includes('base') ? 'base' : network.split('-')[0]) - : 'solana'; - - if (!recipient) { - throw new Error(`No recipient wallet (payTo) found in 402 response.`); - } - - // Log payment quote if verbose logging is enabled - if (this.verbose) { - console.log(' πŸ“‹ Step 1: Received 402 Payment Required'); - console.log(` πŸ’° Cost: ${amountUsdc.toFixed(4)} USDC (${amountMicroUsdc} micro-USDC)`); - console.log(` πŸͺ Pay To: ${recipient}`); - console.log(` πŸ“ Scheme: ${scheme}, Network: ${network} (normalized: ${normalizedNetwork})`); - } - - // Step 3: Determine which wallet to use based on network from 402 response - let paymentConnection = connection; - - // If network is Base/EVM, ensure we have a Base wallet - if (normalizedNetwork === 'base' || normalizedNetwork === 'ethereum' || normalizedNetwork === 'polygon' || normalizedNetwork === 'arbitrum') { - // Check if wallet is already a Base wallet - // Solana Keypair has: publicKey (PublicKey object), secretKey (Uint8Array) - // Base wallet can have: - // - privateKey (string starting with 0x) - required - // - address (string starting with 0x) - optional (can be derived from privateKey) - const isSolanaWallet = wallet?.publicKey && typeof wallet.publicKey.toString === 'function'; - const hasBasePrivateKey = wallet?.privateKey && - typeof wallet.privateKey === 'string' && - (wallet.privateKey.startsWith('0x') || wallet.privateKey.length === 64); - const isBaseWallet = hasBasePrivateKey; // If it has privateKey, it's a Base wallet (address optional) - - if (this.verbose) { - console.log(` πŸ” Wallet detection: isSolanaWallet=${isSolanaWallet}, isBaseWallet=${isBaseWallet}, hasPrivateKey=${!!wallet?.privateKey}`); - } - - if (isSolanaWallet && !isBaseWallet) { - // Wallet is Solana Keypair, need to load Base wallet - try { - paymentWallet = autoDetectBaseWallet(); - if (this.verbose) { - console.log(` πŸ”„ Switched to Base wallet: ${paymentWallet.address}`); - } - } catch (error: any) { - throw new Error( - `Agent requires ${normalizedNetwork} payment but no Base wallet found. ` + - `Set MEMEPUTER_BASE_WALLET_PRIVATE_KEY environment variable or create ~/.memeputer/base-wallet.json. ` + - `Error: ${error.message}` - ); - } - } else if (!isBaseWallet && !isSolanaWallet) { - // Wallet format is unclear, try to load Base wallet - try { - paymentWallet = autoDetectBaseWallet(); - if (this.verbose) { - console.log(` πŸ”„ Loaded Base wallet: ${paymentWallet.address}`); - } - } catch (error: any) { - throw new Error( - `Agent requires ${normalizedNetwork} payment but wallet format is unclear and no Base wallet found. ` + - `Set MEMEPUTER_BASE_WALLET_PRIVATE_KEY environment variable or create ~/.memeputer/base-wallet.json. ` + - `Error: ${error.message}` - ); - } - } - // If wallet already has privateKey in Base format, use it as-is (address optional) - // But ensure paymentWallet is set (it's already initialized to wallet, so this is fine) - - // Final check: ensure paymentWallet has privateKey for Base payments - if (!paymentWallet?.privateKey || (typeof paymentWallet.privateKey !== 'string')) { - // This shouldn't happen, but add defensive check - try { - paymentWallet = autoDetectBaseWallet(); - if (this.verbose) { - console.log(` πŸ”„ Fallback: Loaded Base wallet: ${paymentWallet.address}`); - } - } catch (error: any) { - throw new Error( - `Agent requires ${normalizedNetwork} payment but wallet does not have a valid privateKey. ` + - `Set MEMEPUTER_BASE_WALLET_PRIVATE_KEY environment variable or create ~/.memeputer/base-wallet.json. ` + - `Error: ${error.message}` - ); - } - } - } - - // Step 4: Create and sign payment transaction (pay the quoted amount) - // Pass atomic units directly to avoid floating point precision issues - // Use normalizedNetwork from 402 response, not this.chain - const { signature, transaction, txHash } = await createPaymentTransaction( - paymentConnection, - paymentWallet, - recipient, - amountUsdc, // For display/logging - scheme, - normalizedNetwork, // Use network from 402 response, not this.chain - amountMicroUsdc, // Pass atomic units directly for accurate payment - undefined, // RPC URL (optional, will use defaults) - feePayer, // Pass fee payer from 402 response (for Solana transactions) - ); - paymentHeader = signature; // Store the payment signature - // Store computed transaction hash for Base transactions (backend should return actual hash) - const computedTxHash = txHash; - - // Log payment transaction if verbose logging is enabled - if (this.verbose) { - console.log(' πŸ’Έ Step 2: Creating Payment Transaction'); - console.log(` Amount: ${amountUsdc.toFixed(4)} USDC (${amountMicroUsdc} atomic units)`); - // Handle both Solana (publicKey) and EVM (address or privateKey) wallets - const from = paymentWallet.publicKey?.toString() || paymentWallet.address || 'EVM wallet'; - console.log(` From: ${from}`); - console.log(` To: ${recipient}`); - } - - // Step 5: Retry request with X-PAYMENT header using resource URL from 402 response - // Per x402 spec: "Use the resource URL from the 402 response for the paid request" - // However, if we used a command-specific endpoint, preserve it (backend may return base endpoint) - let resourceUrl: string; - if (acceptDetails.resource) { - let resource = acceptDetails.resource; - - // Fix double /x402 prefix in absolute URLs (e.g., http://localhost:3007/x402/x402/...) - if (resource.includes('/x402/x402/')) { - resource = resource.replace('/x402/x402/', '/x402/'); - if (this.verbose) { - console.log(` πŸ”§ Fixed double /x402 prefix in resource URL`); - } - } - - // If we used a command-specific endpoint, preserve it even if backend returns base endpoint - // This ensures we retry to the correct command-specific endpoint - if (useCommandEndpoint && command) { - // Extract the command-specific path - include /x402 prefix - const commandPath = `/x402/${this.chain}/${agentId}/${command}`; - - // If resource is absolute URL, replace the path with command-specific path - if (resource.startsWith('http://') || resource.startsWith('https://')) { - const url = new URL(resource); - // Preserve protocol and host, use command-specific path - resourceUrl = `${url.protocol}//${url.host}${commandPath}`; - // Always log endpoint preservation (critical for debugging) - console.log(` πŸ”§ Preserving command-specific endpoint: ${resourceUrl}`); - } else { - // Relative path - construct command-specific endpoint using baseUrl - resourceUrl = `${this.baseUrl}/${this.chain}/${agentId}/${command}`; - // Always log endpoint preservation (critical for debugging) - console.log(` πŸ”§ Preserving command-specific endpoint: ${resourceUrl}`); - } - } else { - // Not using command-specific endpoint - use resource as-is - if (resource.startsWith('http://') || resource.startsWith('https://')) { - resourceUrl = resource; - } else { - // Relative path - handle /x402 prefix if present - let resourcePath = resource; - // If resource path starts with /x402, remove it since baseUrl already includes /x402 - if (resourcePath.startsWith('/x402/')) { - resourcePath = resourcePath.substring(6); // Remove '/x402' - } else if (resourcePath.startsWith('/x402')) { - resourcePath = resourcePath.substring(5); // Remove '/x402' - } - // Ensure path starts with / - if (!resourcePath.startsWith('/')) { - resourcePath = '/' + resourcePath; - } - resourceUrl = `${this.baseUrl}${resourcePath}`; - } - } - } else { - // Fallback: use the same URL we used for the initial request - // This preserves command-specific endpoint if we used one - resourceUrl = requestUrl; - } - - // Always log retry URL (critical for debugging endpoint issues) - console.log(' πŸ”„ Step 3: Retrying request with payment'); - console.log(` Resource URL: ${resourceUrl}`); - if (acceptDetails.resource && this.verbose) { - console.log(` Original resource from 402: ${acceptDetails.resource}`); - } - - // Build retry request body - use same format as initial request - // IMPORTANT: Use the exact same construction as the initial request to ensure consistency - let retryBody: Record; - if (useCommandEndpoint && params) { - // Command-specific endpoint: Use params (no command field) - // Use the same construction as initial request to ensure arrays are preserved - retryBody = { ...params }; - // Include message if provided (for backward compatibility) - same as initial request - if (message && message.trim() !== '') { - retryBody.message = message; - } - } else { - // Base endpoint: Use message - retryBody = { message }; - } - - // Log retry body for debugging (always log to help diagnose webhook issues) - console.log(` πŸ“¦ Retry Request Payload:`); - console.log(` ${JSON.stringify(retryBody, null, 2).split('\n').join('\n ')}`); - // Also log array types to verify they're preserved correctly - if (retryBody.qualityModifiers !== undefined) { - const isArray = Array.isArray(retryBody.qualityModifiers); - const allStrings = isArray && retryBody.qualityModifiers.every((item: any) => typeof item === 'string'); - console.log(` πŸ” qualityModifiers validation: Array.isArray=${isArray}, allStrings=${allStrings}, value=${JSON.stringify(retryBody.qualityModifiers)}`); - if (!isArray || !allStrings) { - console.error(` ⚠️ WARNING: qualityModifiers is not an array of strings! This may cause webhook validation errors.`); - } - } - - response = await axios.post( - resourceUrl, - retryBody, - { - headers: { - "Content-Type": "application/json", - "X-PAYMENT": paymentHeader, - "User-Agent": "@memeputer/sdk", - }, - }, - ); - - // Log payment confirmation if verbose logging is enabled - if (this.verbose && response.status === 200) { - console.log(' βœ… Step 3: Payment Confirmed'); - console.log(` Status: ${response.status} OK`); - } - } - - // Parse successful response (after payment) - // Handle empty or invalid responses - if (!response.data) { - throw new Error( - `Empty response from backend. Status: ${response.status}, URL: ${this.baseUrl}/${this.chain}/${agentId}. ` + - `Check that the backend is running and the endpoint exists.` - ); - } - - const data = response.data; - - // Log raw JSON response from API (standardized format) - if (this.verbose) { - console.log('\n πŸ“¦ Raw API Response (JSON):'); - console.log(` ${JSON.stringify(data, null, 2).split('\n').join('\n ')}`); - console.log(''); - } - - // Handle HTTP 202 (async job started) - if (response.status === 202) { - // Async response - return job details - const receipt = data.receipt || data.x402Receipt; - const parsedReceipt = receipt ? await this.parseReceipt(receipt, paymentWallet || wallet, recipient, amountUsdc, amountMicroUsdc, paymentHeader, normalizedNetwork, computedTxHash) : undefined; - - const asyncResponse = { - success: data.success !== false, // Default to true for 202 - response: data.response || "Job started", - format: (data.format || "text") as "text" | "json" | "markdown", - statusUrl: data.statusUrl, - jobId: data.jobId, - etaSeconds: data.etaSeconds, - retryAfterSeconds: data.retryAfterSeconds, // Polling interval (default: 2s) - agentId: data.agentId || agentId, - agentUuid: data.agentUuid, // Internal UUID (optional) - timestamp: data.timestamp, // Server response time - x402Version: data.x402Version, - receipt: parsedReceipt, - x402Receipt: parsedReceipt, // Backward compatibility - }; - - // Log async response structure - if (this.verbose) { - console.log(' πŸ“€ SDK Transformed Response (HTTP 202 Async):'); - console.log(` ${JSON.stringify(asyncResponse, null, 2).split('\n').join('\n ')}`); - console.log(''); - } - - return asyncResponse; - } - - // Step 6: Parse RECEIPT from success response (after payment) - // This is the actual amount paid - use for cost tracking - // Support both new 'receipt' field and legacy 'x402Receipt' field - let receiptData = data.receipt || data.x402Receipt; - let x402Receipt: X402Receipt | undefined; - if (receiptData) { - // RECEIPT: Backend provided actual payment receipt - // Use this for accurate cost tracking (actual amount paid) - x402Receipt = await this.parseReceipt(receiptData, paymentWallet || wallet, recipient, amountUsdc, amountMicroUsdc, paymentHeader, normalizedNetwork, computedTxHash); - } else if (paymentHeader && recipient && amountUsdc !== undefined && amountMicroUsdc !== undefined) { - // Fallback: Construct receipt from quote (until backend adds actual receipt) - // Note: This uses the quoted amount, not actual amount paid - const payerWallet = paymentWallet || wallet; - let payerAddress = payerWallet.publicKey?.toString() || payerWallet.address; - - // If Base wallet doesn't have address, derive it from private key - if (!payerAddress && payerWallet.privateKey && typeof payerWallet.privateKey === 'string') { - try { - const { ethers } = await import('ethers'); - const tempWallet = new ethers.Wallet(payerWallet.privateKey); - payerAddress = tempWallet.address; - } catch { - payerAddress = 'unknown'; - } - } - - payerAddress = payerAddress || 'unknown'; - - // For Base transactions, use computed hash if available - const txSignature = (normalizedNetwork === 'base' && computedTxHash) - ? computedTxHash - : paymentHeader; - - x402Receipt = { - amountPaidUsdc: amountUsdc, // Quote amount (not actual) - amountPaidMicroUsdc: amountMicroUsdc, - payTo: recipient, - transactionSignature: txSignature, - payer: payerAddress, - merchant: recipient, - timestamp: new Date().toISOString(), - }; - } - - // Normalize format field to standardized values - let normalizedFormat: "text" | "json" | "markdown" = "text"; - if (data.format) { - const formatLower = data.format.toLowerCase(); - if (formatLower === "json") { - normalizedFormat = "json"; - } else if (formatLower === "markdown") { - normalizedFormat = "markdown"; - } else { - normalizedFormat = "text"; - } - } - - // Log final transformed response structure - if (this.verbose) { - const transformedResponse = { - success: data.success !== false, - response: data.response || data.message || "", - format: normalizedFormat, - mediaUrl: data.mediaUrl || data.media_url, - statusUrl: data.statusUrl || data.status_url, - imageUrl: data.imageUrl || data.image_url, - etaSeconds: data.etaSeconds || data.eta_seconds, - retryAfterSeconds: data.retryAfterSeconds, - transactionSignature: data.transactionSignature || paymentHeader, - agentId: data.agentId || agentId, - agentUuid: data.agentUuid, - timestamp: data.timestamp, - x402Version: data.x402Version, - receipt: x402Receipt, - x402Receipt, - }; - console.log(' πŸ“€ SDK Transformed Response:'); - console.log(` ${JSON.stringify(transformedResponse, null, 2).split('\n').join('\n ')}`); - console.log(''); - } - - return { - success: data.success !== false, // Default to true, but respect explicit false - response: data.response || data.message || "", - format: normalizedFormat, - mediaUrl: data.mediaUrl || data.media_url, - statusUrl: data.statusUrl || data.status_url, - imageUrl: data.imageUrl || data.image_url, - etaSeconds: data.etaSeconds || data.eta_seconds, - retryAfterSeconds: data.retryAfterSeconds, // Polling interval (for async operations) - transactionSignature: data.transactionSignature || paymentHeader, - agentId: data.agentId || agentId, - agentUuid: data.agentUuid, // Internal UUID (optional) - timestamp: data.timestamp, // Server response time - x402Version: data.x402Version, - receipt: x402Receipt, // Standardized field name - x402Receipt, // Legacy field name (for backward compatibility) - x402Quote: paymentHeader && amountUsdc !== undefined && amountMicroUsdc !== undefined ? { - amountQuotedUsdc: amountUsdc, // Amount we paid (from quote) - amountQuotedMicroUsdc: amountMicroUsdc, - maxAmountRequired: acceptDetails?.maxAmountRequired || amountUsdc, - } : undefined, // Include quote details if payment was made - }; - } catch (error: any) { - // Better error messages - if (error.response) { - const status = error.response.status; - const data = error.response.data; - - // Log raw error response JSON - if (this.verbose) { - console.error(`\n ❌ Error Response (HTTP ${status}):`); - console.error(` ${JSON.stringify(data, null, 2).split('\n').join('\n ')}`); - if (status === 500) { - const failedRequest = useCommandEndpoint && params ? { ...params } : { message }; - console.error(`\n πŸ“¦ Request that failed:`); - console.error(` ${JSON.stringify(failedRequest, null, 2).split('\n').join('\n ')}`); - } - console.log(''); - } - - // Handle standardized error responses - if (status === 402) { - // HTTP 402 Payment Required - unchanged format - const errorMsg = - data?.error || - data?.message || - "Payment required but payment was rejected"; - throw new Error( - `${errorMsg}. Check your USDC balance. If this is a new agent, ensure its USDC token account has been initialized (agent needs ~0.002 SOL for initial setup).`, - ); - } - - if (status === 404) { - const errorMsg = data?.error || data?.message || `Agent endpoint not found`; - const errorCode = data?.code || 'agent_not_found'; - throw new Error( - `${errorMsg} (${errorCode}). Agent ID: "${agentId}", URL: ${this.baseUrl}/${this.chain}/${agentId}. ` + - `Check that the agent exists and the API URL is correct.` - ); - } - - if (data?.error || data?.success === false) { - // Standardized error format: { success: false, error: "...", code: "..." } - const errorMsg = data?.error || data?.message || "Unknown error"; - const errorCode = data?.code; - - // Include more context for 500 errors to help debug backend issues - if (status === 500) { - throw new Error(`Internal server error${errorCode ? ` (${errorCode})` : ''}: ${errorMsg}`); - } - - // Include error code if available - throw new Error(errorCode ? `${errorMsg} (${errorCode})` : errorMsg); - } - - // Log full response for debugging - const errorDetails = typeof data === 'string' - ? data - : JSON.stringify(data, null, 2); - - throw new Error(`API error (${status}): ${errorDetails}`); - } - - throw error; - } - } - - /** - * Helper method to parse receipt from response data - * Supports both new standardized format and legacy format - */ - private async parseReceipt( - receiptData: any, - wallet: any, - recipient: string | undefined, - amountUsdc: number | undefined, - amountMicroUsdc: number | undefined, - paymentHeader: string | undefined, - normalizedNetwork: string, - computedTxHash: string | undefined - ): Promise { - // Get payer address (Solana or EVM wallet) - let payerAddress = wallet.publicKey?.toString() || wallet.address; - - // If Base wallet doesn't have address, derive it from private key - if (!payerAddress && wallet.privateKey && typeof wallet.privateKey === 'string') { - try { - const { ethers } = await import('ethers'); - const tempWallet = new ethers.Wallet(wallet.privateKey); - payerAddress = tempWallet.address; - } catch { - payerAddress = 'unknown'; - } - } - - payerAddress = payerAddress || 'unknown'; - - // For Base transactions, prefer the actual transaction hash from backend - // If backend doesn't provide a valid hash (returns payment header instead), use computed hash - let txSignature = receiptData.transactionSignature; - - // Check if backend returned a valid Base transaction hash (0x + 64 hex chars = 66 chars) - const isValidBaseHash = txSignature && - txSignature.startsWith('0x') && - txSignature.length === 66 && - /^0x[a-fA-F0-9]{64}$/.test(txSignature); - - // If backend didn't return a valid hash, use computed hash for Base transactions - if (normalizedNetwork === 'base' && computedTxHash && !isValidBaseHash) { - // Backend returned payment header or invalid hash, use computed hash instead - txSignature = computedTxHash; - } - - return { - amountPaidUsdc: receiptData.amountPaidUsdc || amountUsdc || 0, - amountPaidMicroUsdc: receiptData.amountPaidMicroUsdc || amountMicroUsdc || 0, - payTo: receiptData.payTo || recipient || '', - transactionSignature: txSignature || paymentHeader || '', - payer: receiptData.payer || payerAddress, - merchant: receiptData.merchant || recipient || '', - timestamp: receiptData.timestamp || new Date().toISOString(), - }; - } - - /** - * Check status of async operation - * Status URLs point to agents-api proxy (no auth required) - * Supports both legacy status field and new state field - */ - async checkStatus(statusUrl: string): Promise { - try { - const response = await axios.get(statusUrl); - // Handle both wrapped (ok()) and unwrapped responses - const data = response.data.data || response.data; - - // Log raw status response JSON - if (this.verbose) { - console.log(`\n πŸ“¦ Status Check Response (${statusUrl}):`); - console.log(` ${JSON.stringify(data, null, 2).split('\n').join('\n ')}`); - console.log(''); - } - - // Map standardized state field to legacy status field for backward compatibility - let status: "pending" | "processing" | "completed" | "failed" = "pending"; - const state = data.state || data.status; - - if (state === "succeeded" || state === "completed") { - status = "completed"; - } else if (state === "failed") { - status = "failed"; - } else if (state === "processing") { - status = "processing"; - } else { - status = "pending"; - } - - const statusResult = { - status, // Legacy field - state: data.state || (state === "completed" ? "succeeded" : state), // Standardized field - message: data.message || data.response, - response: data.response, // Final result content - artifactUrl: data.artifactUrl, - imageUrl: data.imageUrl || data.image_url, - mediaUrl: data.mediaUrl || data.media_url, - error: data.error, - code: data.code, // Machine-readable error code (e.g., "generation_timeout", "generation_failed") - }; - - // Log transformed status result - if (this.verbose) { - console.log(' πŸ“€ SDK Transformed Status Result:'); - console.log(` ${JSON.stringify(statusResult, null, 2).split('\n').join('\n ')}`); - console.log(''); - } - - return statusResult; - } catch (error: any) { - if (error.response?.data) { - // Log error response - if (this.verbose) { - console.error(`\n ❌ Status Check Error Response:`); - console.error(` ${JSON.stringify(error.response.data, null, 2).split('\n').join('\n ')}`); - console.log(''); - } - return { - status: "failed", - state: "failed", - error: error.response.data.error || "Status check failed", - }; - } - throw error; - } - } - - /** - * Poll status URL until completion or timeout - * Uses retryAfterSeconds from initial HTTP 202 response if available, otherwise uses intervalMs - */ - async pollStatus( - statusUrl: string, - options: { - maxAttempts?: number; - intervalMs?: number; - retryAfterSeconds?: number; // Polling interval from HTTP 202 response (default: 2s) - onProgress?: (attempt: number, status: StatusCheckResult) => void; - } = {}, - ): Promise { - const maxAttempts = options.maxAttempts || 60; // 5 minutes at default intervals - // Use retryAfterSeconds from HTTP 202 response if provided, otherwise use intervalMs or default to 2s - const retryAfterSeconds = options.retryAfterSeconds ?? (options.intervalMs ? options.intervalMs / 1000 : 2); - const intervalMs = retryAfterSeconds * 1000; - - for (let attempt = 1; attempt <= maxAttempts; attempt++) { - const result = await this.checkStatus(statusUrl); - - if (options.onProgress) { - options.onProgress(attempt, result); - } - - // Check both legacy status and new state field - const isComplete = result.status === "completed" || result.state === "succeeded"; - const isFailed = result.status === "failed" || result.state === "failed"; - - if (isComplete || isFailed) { - return result; - } - - if (attempt < maxAttempts) { - await new Promise((resolve) => setTimeout(resolve, intervalMs)); - } - } - - return { - status: "failed", - state: "failed", - error: "Timeout waiting for completion", - code: "timeout", - }; - } -} - diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts deleted file mode 100644 index 85bfb6b..0000000 --- a/packages/sdk/src/index.ts +++ /dev/null @@ -1,332 +0,0 @@ -import { Connection, Keypair } from "@solana/web3.js"; -import { AgentsApiClient, InteractionResult, StatusCheckResult } from "./api"; -import { autoDetectWallet, autoDetectRpcUrl, autoDetectApiUrl, autoDetectChain, autoDetectBaseWallet, BaseWallet } from "./utils"; - -export interface MemeputerConfig { - apiUrl?: string; - rpcUrl?: string; - chain?: 'solana' | 'base' | string; // Blockchain to use (default: 'solana') - wallet?: Keypair | any; // Solana Keypair or EVM Wallet - connection?: Connection | any; // Solana Connection or EVM Provider - verbose?: boolean; // Enable verbose logging to show x402 protocol details -} - -export interface PromptOptions { - agentId: string; - message: string; -} - -export interface CommandOptions { - agentId: string; - command: string; - params?: string[] | Record; -} - -export interface PromptResult extends InteractionResult {} -export interface CommandResult extends InteractionResult {} - -/** - * Memeputer SDK - Simple interface for interacting with AI agents via x402 - */ -export class Memeputer { - private apiClient: AgentsApiClient; - private wallet?: Keypair | any; - private connection?: Connection | any; - private rpcUrl?: string; - private apiUrl: string; - private chain: string; - - constructor(config: MemeputerConfig = {}) { - this.chain = config.chain || autoDetectChain(); // Auto-detect or default to Solana - this.apiUrl = config.apiUrl || autoDetectApiUrl(); - this.apiClient = new AgentsApiClient(this.apiUrl, this.chain); - - // Enable verbose logging if requested - if (config.verbose) { - this.apiClient.enableVerbose(); - } - - // Store config - lazy initialize wallet/connection on first use - this.wallet = config.wallet; - this.connection = config.connection; - this.rpcUrl = config.rpcUrl; - } - - /** - * Initialize wallet and connection (called automatically on first use) - */ - private ensureInitialized() { - if (!this.wallet) { - this.wallet = autoDetectWallet(); - } - if (!this.connection) { - const rpc = this.rpcUrl || autoDetectRpcUrl(); - this.connection = new Connection(rpc, "confirmed"); - } - } - - /** - * Prompt an agent with a natural language message - * - * @example - * ```ts - * import memeputer from '@memeputer/sdk'; - * - * // Simple string overload - * const result = await memeputer.prompt("memeputer", "Hello, how are you?"); - * - * // Or object syntax - * const result = await memeputer.prompt({ - * agentId: "memeputer", - * message: "Hello, how are you?" - * }); - * - * console.log(result.response); - * ``` - */ - async prompt(options: PromptOptions | string, message?: string): Promise { - this.ensureInitialized(); - - // Support both object and string overloads - const agentId = typeof options === 'string' ? options : options.agentId; - const msg = typeof options === 'string' ? (message || '') : options.message; - - return this.apiClient.interact( - agentId, - msg, - this.wallet!, - this.connection!, - ); - } - - /** - * Execute a command on an agent - * - * @example - * ```ts - * import memeputer from '@memeputer/sdk'; - * - * // Simple string overload (matches prompt signature) - * const result = await memeputer.command("memeputer", "ping"); - * - * // With positional params - * const result = await memeputer.command("pfpputer", "pfp", ["generate", "a cat"]); - * - * // With named params (object) - * const result = await memeputer.command("pfpputer", "pfp", { style: "anime", subject: "cat" }); - * - * // Or object syntax - * const result = await memeputer.command({ - * agentId: "pfpputer", - * command: "pfp", - * params: ["generate", "a cat wearing sunglasses"] - * }); - * ``` - */ - async command( - options: CommandOptions | string, - command?: string, - params?: string[] | Record - ): Promise { - this.ensureInitialized(); - - let agentId: string; - let cmd: string; - let paramsObj: Record | undefined; - - if (typeof options === 'string') { - // String overload: command(agentId, command, params?) - // Matches prompt signature: prompt(agentId, message) - agentId = options; - cmd = command || ''; - paramsObj = Array.isArray(params) ? undefined : params; - } else { - // Object syntax - agentId = options.agentId; - cmd = options.command; - paramsObj = Array.isArray(options.params) ? undefined : options.params; - } - - // Commands that expect JSON payloads (not CLI format) - only for commands that need JSON even with simple params - const jsonPayloadCommands = ['describe_image', 'generate_captions', 'post_telegram', 'discover_trends', 'enhance_prompt', 'keywords', 'select_best_trend']; - const needsJsonPayload = jsonPayloadCommands.includes(cmd); - - // Check if params contain complex objects (not just primitives) - // Arrays of primitives are considered simple and can be converted to CLI format - const hasComplexParams = paramsObj && Object.values(paramsObj).some(value => { - if (value === null || value === undefined) return false; - if (Array.isArray(value)) { - // Arrays are simple if they only contain primitives - return value.some(item => - typeof item !== 'string' && - typeof item !== 'number' && - typeof item !== 'boolean' && - item !== null - ); - } - // Non-array values are complex if they're not primitives - return typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean'; - }); - - // If command needs JSON or params are complex, use command-specific endpoint - if (needsJsonPayload || hasComplexParams) { - // Use command-specific endpoint for structured commands - // Don't include command in message - it's in the URL - const message = ''; // Empty message for command-specific endpoint - return this.apiClient.interact( - agentId, - message, - this.wallet!, - this.connection!, - cmd, // Pass command name for command-specific endpoint - paramsObj, // Pass structured params - ); - } - - // Otherwise, use CLI format - let cmdParams: string[] = []; - - if (typeof options === 'string') { - if (params) { - if (Array.isArray(params)) { - cmdParams = params; - } else { - // Convert named params object to CLI format: --key value - cmdParams = this.convertParamsToCliArgs(params); - } - } - } else { - if (options.params) { - if (Array.isArray(options.params)) { - cmdParams = options.params; - } else { - // Convert named params object to CLI format: --key value - cmdParams = this.convertParamsToCliArgs(options.params); - } - } - } - - // Build command message with slash prefix - const message = cmdParams.length > 0 - ? `/${cmd} ${cmdParams.join(" ")}` - : `/${cmd}`; - - return this.apiClient.interact( - agentId, - message, - this.wallet!, - this.connection!, - ); - } - - /** - * Helper to convert params object to CLI args array - * Handles camelCase to kebab-case conversion for keys - * Supports positional args via special '_args' or 'args' key - * - * @example - * // Pure flags - * { refImages: ['url1', 'url2'] } -> ['--ref-images', 'url1 url2'] - * - * // Positional args + flags - * { _args: ['generate', 'prompt'], refImages: ['url1'] } -> ['generate', 'prompt', '--ref-images', 'url1'] - */ - private convertParamsToCliArgs(params: Record): string[] { - const positionalArgs: string[] = []; - const flags: string[] = []; - - // Extract positional arguments if present - if ('_args' in params && Array.isArray(params._args)) { - positionalArgs.push(...params._args.map(String)); - } else if ('args' in params && Array.isArray(params.args)) { - positionalArgs.push(...params.args.map(String)); - } - - // Convert all other keys to flags - for (const [key, value] of Object.entries(params)) { - // Skip special keys used for positional args - if (key === '_args' || key === 'args') { - continue; - } - - // Convert camelCase to kebab-case for CLI flags - // e.g. refImages -> --ref-images - const kebabKey = key.replace(/[A-Z]/g, m => "-" + m.toLowerCase()); - const flag = `--${kebabKey}`; - - // Handle array values - spread them as separate arguments after the flag - // e.g. { refImages: ['url1', 'url2'] } -> ['--ref-images', 'url1', 'url2'] - if (Array.isArray(value)) { - flags.push(flag, ...value.map(String)); - } else { - flags.push(flag, String(value)); - } - } - - // Combine: positional args first, then flags - return [...positionalArgs, ...flags]; - } - - /** - * Check status of an async operation - */ - async checkStatus(statusUrl: string): Promise { - return this.apiClient.checkStatus(statusUrl); - } - - /** - * Poll status URL until completion or timeout - * Uses retryAfterSeconds from initial HTTP 202 response if available - */ - async pollStatus( - statusUrl: string, - options?: { - maxAttempts?: number; - intervalMs?: number; - retryAfterSeconds?: number; // Polling interval from HTTP 202 response (default: 2s) - onProgress?: (attempt: number, status: StatusCheckResult) => void; - }, - ): Promise { - return this.apiClient.pollStatus(statusUrl, options); - } - - /** - * List all available agents - */ - async listAgents() { - return this.apiClient.listAgents(); - } - - /** - * Enable verbose logging to show x402 protocol details - */ - enableVerbose() { - this.apiClient.enableVerbose(); - } - - /** - * Disable verbose logging - */ - disableVerbose() { - this.apiClient.disableVerbose(); - } -} - -// Export types -export type { - InteractionResult, - StatusCheckResult, - AgentInfo, - X402Receipt, -} from "./api"; - -// PromptResult and CommandResult are already exported as interfaces above - -export { AgentsApiClient } from "./api"; -export { getUsdcBalance, getBaseUsdcBalance } from "./x402Client"; -export { autoDetectBaseWallet, BaseWallet } from "./utils"; - -// Default export - auto-detects wallet and connection -const memeputer = new Memeputer(); - -export default memeputer; diff --git a/packages/sdk/src/utils.ts b/packages/sdk/src/utils.ts deleted file mode 100644 index 92ed6e3..0000000 --- a/packages/sdk/src/utils.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { Connection, Keypair } from "@solana/web3.js"; -import { readFileSync, existsSync } from "fs"; -import { join } from "path"; -import { homedir } from "os"; -import bs58 from "bs58"; -import { Wallet } from "ethers"; - -/** - * Auto-detect wallet from common locations - */ -export function autoDetectWallet(): Keypair { - // Check environment variables first - const envWallet = process.env.MEMEPUTER_WALLET || - process.env.ORCHESTRATOR_WALLET || - process.env.WALLET_SECRET_KEY; - - if (envWallet) { - // Expand tilde if present - const walletPath = envWallet.startsWith('~/') - ? envWallet.replace('~', homedir()) - : envWallet; - - if (existsSync(walletPath)) { - return loadWalletFromFile(walletPath); - } - - // Try as base58 encoded secret key - try { - return Keypair.fromSecretKey(bs58.decode(envWallet)); - } catch { - // Not base58, try JSON string - try { - const walletData = JSON.parse(envWallet); - return Keypair.fromSecretKey(new Uint8Array(walletData)); - } catch { - throw new Error(`Invalid wallet format: ${envWallet}`); - } - } - } - - // Check config file - const configPath = join(homedir(), ".memeputerrc"); - if (existsSync(configPath)) { - try { - const config = JSON.parse(readFileSync(configPath, "utf-8")); - if (config.wallet) { - const walletPath = config.wallet.startsWith('~/') - ? config.wallet.replace('~', homedir()) - : config.wallet; - if (existsSync(walletPath)) { - return loadWalletFromFile(walletPath); - } - } - } catch { - // Ignore config errors - } - } - - // Default Solana CLI wallet location - const defaultWalletPath = join(homedir(), ".config", "solana", "id.json"); - if (existsSync(defaultWalletPath)) { - return loadWalletFromFile(defaultWalletPath); - } - - throw new Error( - "No wallet found. Set MEMEPUTER_WALLET environment variable or create ~/.config/solana/id.json" - ); -} - -function loadWalletFromFile(walletPath: string): Keypair { - if (!existsSync(walletPath)) { - throw new Error(`Wallet file not found: ${walletPath}`); - } - - try { - const walletData = readFileSync(walletPath, "utf-8"); - const secretKey = JSON.parse(walletData); - - // Handle both array format and base58 string format - if (Array.isArray(secretKey)) { - return Keypair.fromSecretKey(Uint8Array.from(secretKey)); - } else if (typeof secretKey === "string") { - return Keypair.fromSecretKey(bs58.decode(secretKey)); - } else { - throw new Error("Invalid wallet format"); - } - } catch (error: any) { - throw new Error(`Failed to load wallet: ${error.message}`); - } -} - -/** - * Auto-detect RPC URL from environment or config - */ -export function autoDetectRpcUrl(): string { - // Check environment variable - if (process.env.SOLANA_RPC_URL) { - return process.env.SOLANA_RPC_URL; - } - - // Check config file - const configPath = join(homedir(), ".memeputerrc"); - if (existsSync(configPath)) { - try { - const config = JSON.parse(readFileSync(configPath, "utf-8")); - if (config.rpcUrl) { - return config.rpcUrl; - } - } catch { - // Ignore config errors - } - } - - // Default to mainnet public RPC - return "https://api.mainnet-beta.solana.com"; -} - -/** - * Auto-detect API URL from environment or config - */ -export function autoDetectApiUrl(): string { - // Check environment variable - if (process.env.MEMEPUTER_API_URL || process.env.MEMEPUTER_API_BASE) { - return process.env.MEMEPUTER_API_URL || process.env.MEMEPUTER_API_BASE!; - } - - // Check config file - const configPath = join(homedir(), ".memeputerrc"); - if (existsSync(configPath)) { - try { - const config = JSON.parse(readFileSync(configPath, "utf-8")); - if (config.apiUrl) { - return config.apiUrl; - } - } catch { - // Ignore config errors - } - } - - // Default to production - return "https://agents.memeputer.com/x402"; -} - -/** - * Auto-detect blockchain chain from environment or config - */ -export function autoDetectChain(): string { - // Check environment variable - if (process.env.MEMEPUTER_CHAIN) { - return process.env.MEMEPUTER_CHAIN; - } - - // Check config file - const configPath = join(homedir(), ".memeputerrc"); - if (existsSync(configPath)) { - try { - const config = JSON.parse(readFileSync(configPath, "utf-8")); - if (config.chain) { - return config.chain; - } - } catch { - // Ignore config errors - } - } - - // Default to Solana for backward compatibility - return "solana"; -} - -/** - * Base/EVM wallet interface - */ -export interface BaseWallet { - address: string; - privateKey: string; -} - -/** - * Auto-detect Base/EVM wallet from common locations - */ -export function autoDetectBaseWallet(): BaseWallet { - // Check environment variable for private key - const envPrivateKey = process.env.MEMEPUTER_BASE_WALLET_PRIVATE_KEY || - process.env.BASE_WALLET_PRIVATE_KEY || - process.env.EVM_WALLET_PRIVATE_KEY; - - if (envPrivateKey) { - try { - const wallet = new Wallet(envPrivateKey); - return { - address: wallet.address, - privateKey: wallet.privateKey, - }; - } catch (error: any) { - throw new Error(`Invalid Base wallet private key: ${error.message}`); - } - } - - // Check config file - const configPath = join(homedir(), ".memeputerrc"); - if (existsSync(configPath)) { - try { - const config = JSON.parse(readFileSync(configPath, "utf-8")); - if (config.baseWallet?.privateKey) { - const wallet = new Wallet(config.baseWallet.privateKey); - return { - address: wallet.address, - privateKey: wallet.privateKey, - }; - } - } catch { - // Ignore config errors - } - } - - // Default Base wallet location - const defaultBaseWalletPath = join(homedir(), ".memeputer", "base-wallet.json"); - if (existsSync(defaultBaseWalletPath)) { - try { - const walletData = JSON.parse(readFileSync(defaultBaseWalletPath, "utf-8")); - if (walletData.privateKey) { - const wallet = new Wallet(walletData.privateKey); - return { - address: wallet.address, - privateKey: wallet.privateKey, - }; - } - } catch (error: any) { - throw new Error(`Failed to load Base wallet from ${defaultBaseWalletPath}: ${error.message}`); - } - } - - throw new Error( - "No Base wallet found. Set MEMEPUTER_BASE_WALLET_PRIVATE_KEY environment variable or create ~/.memeputer/base-wallet.json" - ); -} - diff --git a/packages/sdk/src/x402Client.ts b/packages/sdk/src/x402Client.ts deleted file mode 100644 index 2adf501..0000000 --- a/packages/sdk/src/x402Client.ts +++ /dev/null @@ -1,415 +0,0 @@ -import { - Connection, - Keypair, - ComputeBudgetProgram, - TransactionInstruction, - TransactionMessage, - VersionedTransaction, -} from "@solana/web3.js"; -import { - getAssociatedTokenAddress, - createTransferCheckedInstruction, - TOKEN_PROGRAM_ID, -} from "@solana/spl-token"; -import { PublicKey } from "@solana/web3.js"; -import { ethers } from "ethers"; -import { randomBytes } from "crypto"; -import { BaseWallet } from "./utils"; - -// USDC mint on Solana mainnet -const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); - -// USDC contract on Base mainnet -const BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; - -// ERC20 ABI (just the transfer function we need) -const ERC20_ABI = [ - "function transfer(address to, uint256 amount) returns (bool)", - "function balanceOf(address owner) view returns (uint256)" -]; - -/** - * Create an EIP-3009 authorization for Base USDC payments - * This format enables PayAI facilitator to pay gas fees on behalf of users - */ -async function createEip3009Authorization( - wallet: any, // ethers Wallet, BaseWallet, or object with privateKey - recipient: string, - amountUsdc: number, - scheme: string = "exact", - network: string = "base", - amountMicroUsdc?: number, - rpcUrl?: string, -): Promise<{ transaction: any; signature: string; txHash?: string }> { - try { - // Get private key from wallet - handle multiple formats - let privateKey: string; - - if (typeof wallet === 'string') { - // Direct private key string - privateKey = wallet; - } else if (wallet?.privateKey) { - // BaseWallet interface or ethers Wallet - privateKey = wallet.privateKey; - } else if (wallet?._signingKey?.privateKey) { - // ethers Wallet internal format - privateKey = wallet._signingKey.privateKey; - } else { - throw new Error('No private key found in wallet. Expected Base/EVM wallet with privateKey property.'); - } - - // Ensure private key starts with 0x - if (!privateKey.startsWith('0x')) { - privateKey = '0x' + privateKey; - } - - // Create ethers wallet - const evmWallet = new ethers.Wallet(privateKey); - - // Connect to Base RPC - const provider = new ethers.JsonRpcProvider( - rpcUrl || 'https://mainnet.base.org' - ); - const connectedWallet = evmWallet.connect(provider); - - // Use atomic units directly if provided - const amount = amountMicroUsdc !== undefined - ? BigInt(Math.floor(amountMicroUsdc)) - : BigInt(Math.floor(amountUsdc * 1_000_000)); - - if (amount <= 0n) { - throw new Error(`Invalid payment amount: ${amount} atomic units`); - } - - // Create USDC contract instance for balance check - const usdcContract = new ethers.Contract( - BASE_USDC_ADDRESS, - ERC20_ABI, - connectedWallet - ); - - // Check USDC balance before creating authorization - const balance = await usdcContract.balanceOf(evmWallet.address); - console.log(` πŸ” Debug: USDC Balance: ${ethers.formatUnits(balance, 6)} USDC (${balance.toString()} atomic units)`); - console.log(` πŸ” Debug: Transfer Amount: ${ethers.formatUnits(amount, 6)} USDC (${amount.toString()} atomic units)`); - console.log(` πŸ” Debug: Has enough? ${balance >= amount}`); - - // Determine chain ID based on network - const chainId = network === 'base' ? 8453 : // Base mainnet - network.includes('sepolia') ? 84532 : // Base Sepolia testnet - 8453; // Default to Base mainnet - - // Create EIP-3009 authorization object - const now = Math.floor(Date.now() / 1000); - const authorization = { - from: evmWallet.address, - to: recipient, - value: amount.toString(), // String format for EIP-712 - validAfter: (now - 60).toString(), // 60 second buffer for clock skew between client, server, and blockchain - validBefore: (now + 300).toString(), // 5 minute window for PayAI to execute - nonce: "0x" + randomBytes(32).toString("hex") // Random 32-byte nonce - }; - - // EIP-712 domain for USDC on Base - // USDC contract name is "USD Coin" and version is "2" per EIP-3009 spec - const domain = { - name: "USD Coin", - version: "2", - chainId: chainId, - verifyingContract: BASE_USDC_ADDRESS // USDC contract address - }; - - // EIP-712 types for TransferWithAuthorization - const 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" } - ] - }; - - // Sign with EIP-712 - const eip712Signature = await connectedWallet.signTypedData(domain, types, authorization); - - // Create X-PAYMENT header in x402 format (EIP-3009) - const paymentPayload = { - x402Version: 1, - scheme: scheme, - network: network, - payload: { - signature: eip712Signature, - authorization: authorization - }, - }; - - // Encode as base64 - const paymentHeader = Buffer.from(JSON.stringify(paymentPayload)).toString('base64'); - - // Note: For EIP-3009, we don't have a transaction hash yet - // The facilitator will execute the transferWithAuthorization and return the hash - return { - transaction: null, // Not used for EIP-3009 - signature: paymentHeader, - txHash: undefined // Will be returned by backend after facilitator executes - }; - } catch (error) { - throw new Error( - `Failed to create EIP-3009 authorization: ${error instanceof Error ? error.message : String(error)}` - ); - } -} - -/** - * Create an EVM USDC payment transaction (for Base and other EVM chains) - * Uses EIP-3009 format for Base to enable PayAI facilitator - */ -async function createEvmPaymentTransaction( - wallet: any, // ethers Wallet, BaseWallet, or object with privateKey - recipient: string, - amountUsdc: number, - scheme: string = "exact", - network: string = "base", - amountMicroUsdc?: number, - rpcUrl?: string, -): Promise<{ transaction: any; signature: string; txHash?: string }> { - // Use EIP-3009 format for Base network - if (network === 'base' || network.startsWith('base-')) { - return createEip3009Authorization( - wallet, - recipient, - amountUsdc, - scheme, - network, - amountMicroUsdc, - rpcUrl - ); - } - - // For other EVM chains, fall back to raw transaction format - // (This can be updated to EIP-3009 later if needed) - throw new Error(`EIP-3009 format not yet implemented for network: ${network}. Base network is supported.`); -} - -/** - * Create a simple USDC payment transaction (for SDK use) - * This creates a signed transaction that can be sent in the X-PAYMENT header - * Supports both Solana and EVM chains (Base, Ethereum, etc.) - */ -export async function createPaymentTransaction( - connection: Connection | any, - payer: Keypair | any, - recipient: string, - amountUsdc: number, - scheme: string = "exact", - network: string = "solana", - amountMicroUsdc?: number, // Optional: pass atomic units directly to avoid floating point precision issues - rpcUrl?: string, // Optional: RPC URL for EVM chains - feePayer?: string, // Optional: Fee payer address from 402 response (for Solana transactions) -): Promise<{ transaction: VersionedTransaction | any; signature: string; txSignature?: string; txHash?: string }> { - // Route to appropriate payment method based on network - if (network === 'base' || network === 'ethereum' || network === 'polygon' || network === 'arbitrum') { - return createEvmPaymentTransaction( - payer, - recipient, - amountUsdc, - scheme, - network, - amountMicroUsdc, - rpcUrl - ); - } - - // Default: Solana payment - try { - const recipientPubkey = new PublicKey(recipient); - - // Use atomic units directly if provided, otherwise convert from decimal USDC - // Prefer atomic units to avoid floating point precision issues - const amount = amountMicroUsdc !== undefined - ? Math.floor(amountMicroUsdc) - : Math.floor(amountUsdc * 1_000_000); - - // Validate: amount must be positive - if (amount <= 0) { - throw new Error(`Invalid payment amount: ${amount} atomic units (from ${amountUsdc} USDC, ${amountMicroUsdc} micro-USDC)`); - } - - // Get token accounts (allowOwnerOffCurve fixes wallet adapter key issues) - const payerTokenAccount = await getAssociatedTokenAddress( - USDC_MINT, - payer.publicKey, - true, // allowOwnerOffCurve - critical for wallet adapter keys! - TOKEN_PROGRAM_ID, - ); - - const recipientTokenAccount = await getAssociatedTokenAddress( - USDC_MINT, - recipientPubkey, - true, // allowOwnerOffCurve - critical for wallet adapter keys! - TOKEN_PROGRAM_ID, - ); - - // Build instructions array - const instructions: TransactionInstruction[] = []; - - // Add ComputeBudget instructions FIRST (required by facilitator!) - instructions.push( - ComputeBudgetProgram.setComputeUnitLimit({ - units: 40_000, - }), - ); - - instructions.push( - ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: 1, - }), - ); - - // Add the transfer instruction (use checked version like agent-to-agent payments) - instructions.push( - createTransferCheckedInstruction( - payerTokenAccount, - USDC_MINT, - recipientTokenAccount, - payer.publicKey, - amount, - 6, // USDC has 6 decimals - [], - TOKEN_PROGRAM_ID, - ), - ); - - // Get recent blockhash - const { blockhash } = await connection.getLatestBlockhash("confirmed"); - - // Set facilitator as fee payer for gas-free transactions! - // Use fee payer from 402 response if provided, otherwise fall back to default - const feePayerAddress = feePayer || "561oabzy81vXYYbs1ZHR1bvpiEr6Nbfd6PGTxPshoz4p"; - const facilitatorPublicKey = new PublicKey(feePayerAddress); - - // Create VersionedTransaction (x402 standard) - const message = new TransactionMessage({ - payerKey: facilitatorPublicKey, - recentBlockhash: blockhash, - instructions, - }).compileToV0Message(); - - const transaction = new VersionedTransaction(message); - - // Find which signature slot corresponds to the user's public key - // The facilitator is at index 0 (fee payer), user is at a different index - const userPubkey = payer.publicKey; - const staticAccountKeys = transaction.message.staticAccountKeys; - const userSignatureIndex = staticAccountKeys.findIndex((key) => - key.equals(userPubkey), - ); - - // Sign with user's wallet (facilitator will add its signature) - transaction.sign([payer]); - - // Serialize transaction - const serialized = transaction.serialize(); - const serializedTx = Buffer.from(serialized).toString("base64"); - - // Extract the user's signature from the correct slot (bs58 encoded) - const bs58 = await import("bs58"); - const userSignature = - userSignatureIndex >= 0 && - transaction.signatures?.[userSignatureIndex] && - !transaction.signatures[userSignatureIndex].every((b) => b === 0) - ? bs58.default.encode(transaction.signatures[userSignatureIndex]) - : undefined; - - // Create X-PAYMENT header in x402 format (minimal format matching agent-to-agent) - // This matches the exact format from x402-solana - const paymentPayload = { - x402Version: 1, - scheme: scheme, - network: network, - payload: { - transaction: serializedTx, - signature: userSignature, // Include signature for reference (optional) - }, - }; - - // Encode as base64 - const paymentHeader = Buffer.from(JSON.stringify(paymentPayload)).toString( - "base64", - ); - - return { transaction, signature: paymentHeader }; - } catch (error) { - throw new Error( - `Failed to create payment: ${error instanceof Error ? error.message : String(error)}`, - ); - } -} - -/** - * Get USDC balance for a Solana wallet - */ -export async function getUsdcBalance( - connection: Connection, - keypair: Keypair, -): Promise { - try { - const tokenAccount = await getAssociatedTokenAddress( - USDC_MINT, - keypair.publicKey, - true, // allowOwnerOffCurve - critical for wallet adapter keys! - TOKEN_PROGRAM_ID, - ); - - const balance = await connection.getTokenAccountBalance(tokenAccount); - return parseFloat(balance.value.uiAmount?.toString() || "0"); - } catch (error) { - // Token account doesn't exist or other error - return 0; - } -} - -/** - * Get USDC balance for a Base/EVM wallet - */ -export async function getBaseUsdcBalance( - wallet: BaseWallet | { address: string; privateKey: string } | string, - rpcUrl?: string, -): Promise { - try { - let address: string; - - if (typeof wallet === 'string') { - // Assume it's an address - address = wallet; - } else if (wallet?.address) { - address = wallet.address; - } else { - // Derive address from private key - const privateKey = wallet.privateKey.startsWith('0x') - ? wallet.privateKey - : '0x' + wallet.privateKey; - const evmWallet = new ethers.Wallet(privateKey); - address = evmWallet.address; - } - - const provider = new ethers.JsonRpcProvider( - rpcUrl || 'https://mainnet.base.org' - ); - - const usdcContract = new ethers.Contract( - BASE_USDC_ADDRESS, - ERC20_ABI, - provider - ); - - const balance = await usdcContract.balanceOf(address); - return parseFloat(ethers.formatUnits(balance, 6)); - } catch (error) { - // Return 0 on error - return 0; - } -} - diff --git a/packages/sdk/tsconfig.json b/packages/sdk/tsconfig.json deleted file mode 100644 index a4b23f9..0000000 --- a/packages/sdk/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "node", - "lib": ["ES2022"], - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} - diff --git a/packages/sdk/vitest.config.ts b/packages/sdk/vitest.config.ts deleted file mode 100644 index 9fe1d91..0000000 --- a/packages/sdk/vitest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - test: { - globals: true, - environment: 'node', - }, -}); - diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a928ddf..01449f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true @@ -7,7785 +7,2112 @@ settings: importers: .: - devDependencies: - '@changesets/cli': - specifier: ^2.27.0 - version: 2.29.7 - '@solana/web3.js': - specifier: ^1.98.4 - version: 1.98.4(typescript@5.9.3) - '@typescript-eslint/eslint-plugin': - specifier: ^7.0.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0)(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/parser': - specifier: ^7.0.0 - version: 7.18.0(eslint@8.57.1)(typescript@5.9.3) - eslint: - specifier: ^8.57.0 - version: 8.57.1 - ethers: - specifier: ^6.13.0 - version: 6.15.0 - husky: - specifier: ^9.1.7 - version: 9.1.7 - lint-staged: - specifier: ^16.2.6 - version: 16.2.6 - prettier: - specifier: ^3.3.0 - version: 3.6.2 - tsx: - specifier: ^4.20.6 - version: 4.20.6 - typescript: - specifier: ^5.9.0 - version: 5.9.3 - - examples/hello-world: - dependencies: - '@memeputer/sdk': - specifier: ^1.8.0 - version: link:../../packages/sdk - '@solana/web3.js': - specifier: ^1.98.4 - version: 1.98.4(typescript@5.9.3) - bs58: - specifier: ^6.0.0 - version: 6.0.0 - dotenv: - specifier: ^16.4.5 - version: 16.6.1 - devDependencies: - '@types/node': - specifier: ^20.11.30 - version: 20.19.24 - tsx: - specifier: ^4.16.0 - version: 4.20.6 - typescript: - specifier: ^5.6.3 - version: 5.9.3 - - examples/integration-test: - dependencies: - '@memeputer/sdk': - specifier: ^1.8.0 - version: link:../../packages/sdk - '@solana/web3.js': - specifier: ^1.98.4 - version: 1.98.4(typescript@5.9.3) - bs58: - specifier: ^6.0.0 - version: 6.0.0 - dotenv: - specifier: ^16.4.5 - version: 16.6.1 - devDependencies: - '@types/node': - specifier: ^20.11.30 - version: 20.19.24 - tsx: - specifier: ^4.16.0 - version: 4.20.6 - typescript: - specifier: ^5.6.3 - version: 5.9.3 - - examples/integration-test-command-formats: - dependencies: - '@memeputer/sdk': - specifier: ^1.8.0 - version: link:../../packages/sdk - '@solana/web3.js': - specifier: ^1.98.4 - version: 1.98.4(typescript@5.9.3) - bs58: - specifier: ^6.0.0 - version: 6.0.0 - dotenv: - specifier: ^16.4.5 - version: 16.6.1 - devDependencies: - '@types/node': - specifier: ^20.11.30 - version: 20.19.24 - tsx: - specifier: ^4.16.0 - version: 4.20.6 - typescript: - specifier: ^5.6.3 - version: 5.9.3 - - examples/marketputer: - dependencies: - '@memeputer/sdk': - specifier: ^1.8.0 - version: link:../../packages/sdk - '@solana/web3.js': - specifier: ^1.98.4 - version: 1.98.4(typescript@5.9.3) - axios: - specifier: ^1.7.7 - version: 1.13.2 - bs58: - specifier: ^6.0.0 - version: 6.0.0 - commander: - specifier: ^12.1.0 - version: 12.1.0 - dotenv: - specifier: ^16.4.5 - version: 16.6.1 - ora: - specifier: ^8.0.1 - version: 8.2.0 - zod: - specifier: ^4.1.12 - version: 4.1.12 - devDependencies: - '@types/node': - specifier: ^20.11.30 - version: 20.19.24 - '@vitest/ui': - specifier: ^4.0.8 - version: 4.0.8(vitest@4.0.8) - tsx: - specifier: ^4.16.0 - version: 4.20.6 - typescript: - specifier: ^5.6.3 - version: 5.9.3 - vitest: - specifier: ^4.0.8 - version: 4.0.8(@types/node@20.19.24)(@vitest/ui@4.0.8)(tsx@4.20.6) - - packages/cli: dependencies: - '@memeputer/sdk': - specifier: workspace:* - version: link:../sdk - '@solana/web3.js': - specifier: ^1.98.4 - version: 1.98.4(typescript@5.9.3) - axios: - specifier: ^1.7.7 - version: 1.13.2 bs58: specifier: ^6.0.0 version: 6.0.0 - chalk: - specifier: ^5.3.0 - version: 5.6.2 - commander: - specifier: ^11.1.0 - version: 11.1.0 - dotenv: - specifier: ^16.3.1 - version: 16.6.1 - ora: - specifier: ^8.0.1 - version: 8.2.0 + tweetnacl: + specifier: ^1.0.3 + version: 1.0.3 devDependencies: - '@types/node': - specifier: ^20.10.6 - version: 20.19.24 - '@vitest/ui': - specifier: ^4.0.8 - version: 4.0.8(vitest@4.0.8) - tsx: - specifier: ^4.7.0 - version: 4.20.6 - typescript: - specifier: ^5.3.3 - version: 5.9.3 - vitest: - specifier: ^4.0.8 - version: 4.0.8(@types/node@20.19.24)(@vitest/ui@4.0.8)(tsx@4.20.6) - - packages/sdk: - dependencies: - '@solana/spl-token': - specifier: ^0.4.14 - version: 0.4.14(@solana/web3.js@1.98.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@coral-xyz/anchor': + specifier: ^0.32.1 + version: 0.32.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) + '@openfacilitator/sdk': + specifier: 1.0.0 + version: 1.0.0 '@solana/web3.js': - specifier: ^1.98.4 - version: 1.98.4(typescript@5.9.3) - axios: - specifier: ^1.6.5 - version: 1.13.2 - bs58: - specifier: ^5.0.0 - version: 5.0.0 - ethers: - specifier: ^6.13.0 - version: 6.15.0 - x402-solana: - specifier: ^0.1.4 - version: 0.1.4(@solana/spl-token@0.4.14)(@solana/sysvars@2.3.0)(@solana/web3.js@1.98.4)(@tanstack/react-query@5.90.7)(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(ws@8.18.3)(zod@4.1.12) - devDependencies: + specifier: ^1.98.0 + version: 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) '@types/node': - specifier: ^20.10.6 - version: 20.19.24 - '@vitest/ui': - specifier: ^4.0.8 - version: 4.0.8(vitest@4.0.8) + specifier: ^22.0.0 + version: 22.19.19 + socket.io-client: + specifier: ^4.8.3 + version: 4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + tsup: + specifier: ^8.0.0 + version: 8.5.1(postcss@8.5.15)(typescript@5.9.3) typescript: - specifier: ^5.3.3 + specifier: ^5.6.0 version: 5.9.3 vitest: - specifier: ^4.0.8 - version: 4.0.8(@types/node@20.19.24)(@vitest/ui@4.0.8)(tsx@4.20.6) + specifier: ^2.0.0 + version: 2.1.9(@types/node@22.19.19) packages: - /@adraffy/ens-normalize@1.10.1: - resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} - - /@adraffy/ens-normalize@1.11.1: - resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} - dev: false - - /@babel/runtime@7.28.4: - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} - /@base-org/account@2.4.0(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(ws@8.18.3)(zod@3.25.76): - resolution: {integrity: sha512-A4Umpi8B9/pqR78D1Yoze4xHyQaujioVRqqO3d6xuDFw9VRtjg6tK3bPlwE0aW+nVH/ntllCpPa2PbI8Rnjcug==} - dependencies: - '@coinbase/cdp-sdk': 1.38.5(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@noble/hashes': 1.4.0 - clsx: 1.2.1 - eventemitter3: 5.0.1 - idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) - preact: 10.24.2 - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - zustand: 5.0.3(react@18.3.1)(use-sync-external-store@1.4.0) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - encoding - - fastestsmallesttextencoderdecoder - - immer - - react - - typescript - - use-sync-external-store - - utf-8-validate - - ws - - zod - dev: false - - /@changesets/apply-release-plan@7.0.13: - resolution: {integrity: sha512-BIW7bofD2yAWoE8H4V40FikC+1nNFEKBisMECccS16W1rt6qqhNTBDmIw5HaqmMgtLNz9e7oiALiEUuKrQ4oHg==} - dependencies: - '@changesets/config': 3.1.1 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.7.3 - dev: true - - /@changesets/assemble-release-plan@6.0.9: - resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.7.3 - dev: true - - /@changesets/changelog-git@0.2.1: - resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - dependencies: - '@changesets/types': 6.1.0 - dev: true - - /@changesets/cli@2.29.7: - resolution: {integrity: sha512-R7RqWoaksyyKXbKXBTbT4REdy22yH81mcFK6sWtqSanxUCbUi9Uf+6aqxZtDQouIqPdem2W56CdxXgsxdq7FLQ==} - hasBin: true - dependencies: - '@changesets/apply-release-plan': 7.0.13 - '@changesets/assemble-release-plan': 6.0.9 - '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.1 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.13 - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3 - '@manypkg/get-packages': 1.1.3 - ansi-colors: 4.1.3 - ci-info: 3.9.0 - enquirer: 2.4.1 - fs-extra: 7.0.1 - mri: 1.2.0 - p-limit: 2.3.0 - package-manager-detector: 0.2.11 - picocolors: 1.1.1 - resolve-from: 5.0.0 - semver: 7.7.3 - spawndamnit: 3.0.1 - term-size: 2.2.1 - transitivePeerDependencies: - - '@types/node' - dev: true - - /@changesets/config@3.1.1: - resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/logger': 0.1.1 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.8 - dev: true - - /@changesets/errors@0.2.0: - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - dependencies: - extendable-error: 0.1.7 - dev: true - - /@changesets/get-dependents-graph@2.1.3: - resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.1 - semver: 7.7.3 - dev: true - - /@changesets/get-release-plan@4.0.13: - resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==} - dependencies: - '@changesets/assemble-release-plan': 6.0.9 - '@changesets/config': 3.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - dev: true - - /@changesets/get-version-range-type@0.4.0: - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - dev: true - - /@changesets/git@3.0.4: - resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} - dependencies: - '@changesets/errors': 0.2.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.8 - spawndamnit: 3.0.1 - dev: true - - /@changesets/logger@0.1.1: - resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - dependencies: - picocolors: 1.1.1 - dev: true - - /@changesets/parse@0.4.1: - resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} - dependencies: - '@changesets/types': 6.1.0 - js-yaml: 3.14.1 - dev: true - - /@changesets/pre@2.0.2: - resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - dev: true - - /@changesets/read@0.6.5: - resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==} - dependencies: - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.1 - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.1 - dev: true - - /@changesets/should-skip-package@0.1.2: - resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - dev: true - - /@changesets/types@4.1.0: - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - dev: true - - /@changesets/types@6.1.0: - resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} - dev: true - - /@changesets/write@0.4.0: - resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - dependencies: - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - human-id: 4.1.2 - prettier: 2.8.8 - dev: true - - /@coinbase/cdp-sdk@1.38.5(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-j8mvx1wMox/q2SjB7C09HtdRXVOpGpfkP7nG4+OjdowPj8pmQ03rigzycd86L8mawl6TUPXdm41YSQVmtc8SzQ==} - dependencies: - '@solana-program/system': 0.8.1(@solana/kit@3.0.3) - '@solana-program/token': 0.6.0(@solana/kit@3.0.3) - '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@solana/web3.js': 1.98.4(typescript@5.9.3) - abitype: 1.0.6(typescript@5.9.3)(zod@3.25.76) - axios: 1.13.2 - axios-retry: 4.5.0(axios@1.13.2) - jose: 6.1.1 - md5: 2.3.0 - uncrypto: 0.1.3 - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - zod: 3.25.76 - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - - ws - dev: false - - /@coinbase/wallet-sdk@3.9.3: - resolution: {integrity: sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw==} - dependencies: - bn.js: 5.2.2 - buffer: 6.0.3 - clsx: 1.2.1 - eth-block-tracker: 7.1.0 - eth-json-rpc-filters: 6.0.1 - eventemitter3: 5.0.1 - keccak: 3.0.4 - preact: 10.27.2 - sha.js: 2.4.12 - transitivePeerDependencies: - - supports-color - dev: false + '@coral-xyz/anchor-errors@0.31.1': + resolution: {integrity: sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==} + engines: {node: '>=10'} - /@coinbase/wallet-sdk@4.3.6(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(zod@3.25.76): - resolution: {integrity: sha512-4q8BNG1ViL4mSAAvPAtpwlOs1gpC+67eQtgIwNvT3xyeyFFd+guwkc8bcX5rTmQhXpqnhzC4f0obACbP9CqMSA==} - dependencies: - '@noble/hashes': 1.4.0 - clsx: 1.2.1 - eventemitter3: 5.0.1 - idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) - preact: 10.24.2 - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - zustand: 5.0.3(react@18.3.1)(use-sync-external-store@1.4.0) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - immer - - react - - typescript - - use-sync-external-store - - utf-8-validate - - zod - dev: false + '@coral-xyz/anchor@0.32.1': + resolution: {integrity: sha512-zAyxFtfeje2FbMA1wzgcdVs7Hng/MijPKpRijoySPCicnvcTQs/+dnPZ/cR+LcXM9v9UYSyW81uRNYZtN5G4yg==} + engines: {node: '>=17'} - /@ecies/ciphers@0.2.5(@noble/ciphers@1.3.0): - resolution: {integrity: sha512-GalEZH4JgOMHYYcYmVqnFirFsjZHeoGMDt9IxEnM9F7GRUUyUksJ7Ou53L83WHJq3RWKD3AcBpo0iQh0oMpf8A==} - engines: {bun: '>=1', deno: '>=2', node: '>=16'} + '@coral-xyz/borsh@0.31.1': + resolution: {integrity: sha512-9N8AU9F0ubriKfNE3g1WF0/4dtlGXoBN/hd1PvbNBamBNwRgHxH4P+o3Zt7rSEloW1HUs6LfZEchlx9fW7POYw==} + engines: {node: '>=10'} peerDependencies: - '@noble/ciphers': ^1.0.0 - dependencies: - '@noble/ciphers': 1.3.0 - dev: false + '@solana/web3.js': ^1.69.0 + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] - /@esbuild/aix-ppc64@0.25.12: - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm64@0.25.12: - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm@0.25.12: - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-x64@0.25.12: - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} cpu: [x64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-arm64@0.25.12: - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-x64@0.25.12: - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-arm64@0.25.12: - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-x64@0.25.12: - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm64@0.25.12: - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm@0.25.12: - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ia32@0.25.12: - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-loong64@0.25.12: - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-mips64el@0.25.12: - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ppc64@0.25.12: - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-riscv64@0.25.12: - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-s390x@0.25.12: - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-x64@0.25.12: - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-arm64@0.25.12: - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-x64@0.25.12: - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-arm64@0.25.12: - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-x64@0.25.12: - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openharmony-arm64@0.25.12: - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - requiresBuild: true - dev: true - optional: true - /@esbuild/sunos-x64@0.25.12: - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-arm64@0.25.12: - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-ia32@0.25.12: - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-x64@0.25.12: - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} engines: {node: '>=18'} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@eslint-community/eslint-utils@4.9.0(eslint@8.57.1): - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.57.1 - eslint-visitor-keys: 3.4.3 - dev: true - - /@eslint-community/regexpp@4.12.2: - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true - - /@eslint/eslintrc@2.1.4: - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.4.3 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - /@eslint/js@8.57.1: - resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} - /@ethereumjs/common@3.2.0: - resolution: {integrity: sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==} - dependencies: - '@ethereumjs/util': 8.1.0 - crc-32: 1.2.2 - dev: false + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - /@ethereumjs/rlp@4.0.1: - resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} - engines: {node: '>=14'} - hasBin: true - dev: false + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - /@ethereumjs/tx@4.2.0: - resolution: {integrity: sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==} - engines: {node: '>=14'} - dependencies: - '@ethereumjs/common': 3.2.0 - '@ethereumjs/rlp': 4.0.1 - '@ethereumjs/util': 8.1.0 - ethereum-cryptography: 2.2.1 - dev: false - - /@ethereumjs/util@8.1.0: - resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==} - engines: {node: '>=14'} - dependencies: - '@ethereumjs/rlp': 4.0.1 - ethereum-cryptography: 2.2.1 - micro-ftch: 0.3.1 - dev: false - - /@gemini-wallet/core@0.3.1(viem@2.38.6): - resolution: {integrity: sha512-XP+/NRAaRV7Adlt6aFxrF/3a0i3qpxFTSVm/dzG+mceXTSgIGOUUT65w69ttLQ/GWEcAEQL/hKoV6PFAJA/DmQ==} - peerDependencies: - viem: '>=2.0.0' - dependencies: - '@metamask/rpc-errors': 7.0.2 - eventemitter3: 5.0.1 - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - transitivePeerDependencies: - - supports-color - dev: false - - /@humanwhocodes/config-array@0.13.0: - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@humanwhocodes/module-importer@1.0.1: - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - dev: true - - /@humanwhocodes/object-schema@2.0.3: - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - dev: true - - /@inquirer/external-editor@1.0.3: - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.0 - dev: true - - /@jridgewell/sourcemap-codec@1.5.5: - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - dev: true - - /@lit-labs/ssr-dom-shim@1.4.0: - resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==} - dev: false - - /@lit/reactive-element@2.1.1: - resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==} - dependencies: - '@lit-labs/ssr-dom-shim': 1.4.0 - dev: false - - /@manypkg/find-root@1.1.0: - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} - dependencies: - '@babel/runtime': 7.28.4 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - dev: true - - /@manypkg/get-packages@1.1.3: - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - dependencies: - '@babel/runtime': 7.28.4 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - dev: true - - /@metamask/eth-json-rpc-provider@1.0.1: - resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==} - engines: {node: '>=14.0.0'} - dependencies: - '@metamask/json-rpc-engine': 7.3.3 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 5.0.2 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/json-rpc-engine@7.3.3: - resolution: {integrity: sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==} - engines: {node: '>=16.0.0'} - dependencies: - '@metamask/rpc-errors': 6.4.0 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/json-rpc-engine@8.0.2: - resolution: {integrity: sha512-IoQPmql8q7ABLruW7i4EYVHWUbF74yrp63bRuXV5Zf9BQwcn5H9Ww1eLtROYvI1bUXwOiHZ6qT5CWTrDc/t/AA==} - engines: {node: '>=16.0.0'} - dependencies: - '@metamask/rpc-errors': 6.4.0 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/json-rpc-middleware-stream@7.0.2: - resolution: {integrity: sha512-yUdzsJK04Ev98Ck4D7lmRNQ8FPioXYhEUZOMS01LXW8qTvPGiRVXmVltj2p4wrLkh0vW7u6nv0mNl5xzC5Qmfg==} - engines: {node: '>=16.0.0'} - dependencies: - '@metamask/json-rpc-engine': 8.0.2 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 - readable-stream: 3.6.2 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/object-multiplex@2.1.0: - resolution: {integrity: sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA==} - engines: {node: ^16.20 || ^18.16 || >=20} - dependencies: - once: 1.4.0 - readable-stream: 3.6.2 - dev: false - - /@metamask/onboarding@1.0.1: - resolution: {integrity: sha512-FqHhAsCI+Vacx2qa5mAFcWNSrTcVGMNjzxVgaX8ECSny/BJ9/vgXP9V7WF/8vb9DltPeQkxr+Fnfmm6GHfmdTQ==} - dependencies: - bowser: 2.12.1 - dev: false - - /@metamask/providers@16.1.0: - resolution: {integrity: sha512-znVCvux30+3SaUwcUGaSf+pUckzT5ukPRpcBmy+muBLC0yaWnBcvDqGfcsw6CBIenUdFrVoAFa8B6jsuCY/a+g==} - engines: {node: ^18.18 || >=20} - dependencies: - '@metamask/json-rpc-engine': 8.0.2 - '@metamask/json-rpc-middleware-stream': 7.0.2 - '@metamask/object-multiplex': 2.1.0 - '@metamask/rpc-errors': 6.4.0 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 - detect-browser: 5.3.0 - extension-port-stream: 3.0.0 - fast-deep-equal: 3.1.3 - is-stream: 2.0.1 - readable-stream: 3.6.2 - webextension-polyfill: 0.10.0 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/rpc-errors@6.4.0: - resolution: {integrity: sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==} - engines: {node: '>=16.0.0'} - dependencies: - '@metamask/utils': 9.3.0 - fast-safe-stringify: 2.1.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/rpc-errors@7.0.2: - resolution: {integrity: sha512-YYYHsVYd46XwY2QZzpGeU4PSdRhHdxnzkB8piWGvJW2xbikZ3R+epAYEL4q/K8bh9JPTucsUdwRFnACor1aOYw==} - engines: {node: ^18.20 || ^20.17 || >=22} - dependencies: - '@metamask/utils': 11.8.1 - fast-safe-stringify: 2.1.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/safe-event-emitter@2.0.0: - resolution: {integrity: sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==} - dev: false - - /@metamask/safe-event-emitter@3.1.2: - resolution: {integrity: sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA==} - engines: {node: '>=12.0.0'} - dev: false - - /@metamask/sdk-analytics@0.0.5: - resolution: {integrity: sha512-fDah+keS1RjSUlC8GmYXvx6Y26s3Ax1U9hGpWb6GSY5SAdmTSIqp2CvYy6yW0WgLhnYhW+6xERuD0eVqV63QIQ==} - dependencies: - openapi-fetch: 0.13.8 - dev: false - - /@metamask/sdk-communication-layer@0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.16)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1): - resolution: {integrity: sha512-0bI9hkysxcfbZ/lk0T2+aKVo1j0ynQVTuB3sJ5ssPWlz+Z3VwveCkP1O7EVu1tsVVCb0YV5WxK9zmURu2FIiaA==} - peerDependencies: - cross-fetch: ^4.0.0 - eciesjs: '*' - eventemitter2: ^6.4.9 - readable-stream: ^3.6.2 - socket.io-client: ^4.5.1 - dependencies: - '@metamask/sdk-analytics': 0.0.5 - bufferutil: 4.0.9 - cross-fetch: 4.1.0 - date-fns: 2.30.0 - debug: 4.3.4 - eciesjs: 0.4.16 - eventemitter2: 6.4.9 - readable-stream: 3.6.2 - socket.io-client: 4.8.1 - utf-8-validate: 5.0.10 - uuid: 8.3.2 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/sdk-install-modal-web@0.32.1: - resolution: {integrity: sha512-MGmAo6qSjf1tuYXhCu2EZLftq+DSt5Z7fsIKr2P+lDgdTPWgLfZB1tJKzNcwKKOdf6q9Qmmxn7lJuI/gq5LrKw==} - dependencies: - '@paulmillr/qr': 0.2.1 - dev: false - - /@metamask/sdk@0.33.1: - resolution: {integrity: sha512-1mcOQVGr9rSrVcbKPNVzbZ8eCl1K0FATsYH3WJ/MH4WcZDWGECWrXJPNMZoEAkLxWiMe8jOQBumg2pmcDa9zpQ==} - dependencies: - '@babel/runtime': 7.28.4 - '@metamask/onboarding': 1.0.1 - '@metamask/providers': 16.1.0 - '@metamask/sdk-analytics': 0.0.5 - '@metamask/sdk-communication-layer': 0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.16)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1) - '@metamask/sdk-install-modal-web': 0.32.1 - '@paulmillr/qr': 0.2.1 - bowser: 2.12.1 - cross-fetch: 4.1.0 - debug: 4.3.4 - eciesjs: 0.4.16 - eth-rpc-errors: 4.0.3 - eventemitter2: 6.4.9 - obj-multiplex: 1.0.0 - pump: 3.0.3 - readable-stream: 3.6.2 - socket.io-client: 4.8.1 - tslib: 2.8.1 - util: 0.12.5 - uuid: 8.3.2 - transitivePeerDependencies: - - bufferutil - - encoding - - supports-color - - utf-8-validate - dev: false - - /@metamask/superstruct@3.2.1: - resolution: {integrity: sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==} - engines: {node: '>=16.0.0'} - dev: false - - /@metamask/utils@11.8.1: - resolution: {integrity: sha512-DIbsNUyqWLFgqJlZxi1OOCMYvI23GqFCvNJAtzv8/WXWzJfnJnvp1M24j7VvUe3URBi3S86UgQ7+7aWU9p/cnQ==} - engines: {node: ^18.18 || ^20.14 || >=22} - dependencies: - '@ethereumjs/tx': 4.2.0 - '@metamask/superstruct': 3.2.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - '@types/debug': 4.1.12 - '@types/lodash': 4.17.20 - debug: 4.4.3 - lodash: 4.17.21 - pony-cause: 2.1.11 - semver: 7.7.3 - uuid: 9.0.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/utils@5.0.2: - resolution: {integrity: sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g==} - engines: {node: '>=14.0.0'} - dependencies: - '@ethereumjs/tx': 4.2.0 - '@types/debug': 4.1.12 - debug: 4.4.3 - semver: 7.7.3 - superstruct: 1.0.4 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/utils@8.5.0: - resolution: {integrity: sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==} - engines: {node: '>=16.0.0'} - dependencies: - '@ethereumjs/tx': 4.2.0 - '@metamask/superstruct': 3.2.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - '@types/debug': 4.1.12 - debug: 4.4.3 - pony-cause: 2.1.11 - semver: 7.7.3 - uuid: 9.0.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@metamask/utils@9.3.0: - resolution: {integrity: sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==} - engines: {node: '>=16.0.0'} - dependencies: - '@ethereumjs/tx': 4.2.0 - '@metamask/superstruct': 3.2.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - '@types/debug': 4.1.12 - debug: 4.4.3 - pony-cause: 2.1.11 - semver: 7.7.3 - uuid: 9.0.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@noble/ciphers@1.2.1: - resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==} - engines: {node: ^14.21.3 || >=16} - dev: false - - /@noble/ciphers@1.3.0: - resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} - engines: {node: ^14.21.3 || >=16} - dev: false - - /@noble/curves@1.2.0: - resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} - dependencies: - '@noble/hashes': 1.3.2 - - /@noble/curves@1.4.2: - resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} - dependencies: - '@noble/hashes': 1.4.0 - dev: false - - /@noble/curves@1.8.0: - resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} - engines: {node: ^14.21.3 || >=16} - dependencies: - '@noble/hashes': 1.7.0 - dev: false - - /@noble/curves@1.8.1: - resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} - engines: {node: ^14.21.3 || >=16} - dependencies: - '@noble/hashes': 1.7.1 - dev: false - - /@noble/curves@1.9.1: - resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} - engines: {node: ^14.21.3 || >=16} - dependencies: - '@noble/hashes': 1.8.0 - dev: false - - /@noble/curves@1.9.7: + '@noble/curves@1.9.7': resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} engines: {node: ^14.21.3 || >=16} - dependencies: - '@noble/hashes': 1.8.0 - - /@noble/hashes@1.3.2: - resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} - engines: {node: '>= 16'} - - /@noble/hashes@1.4.0: - resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} - engines: {node: '>= 16'} - dev: false - - /@noble/hashes@1.7.0: - resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==} - engines: {node: ^14.21.3 || >=16} - dev: false - - /@noble/hashes@1.7.1: - resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} - engines: {node: ^14.21.3 || >=16} - dev: false - /@noble/hashes@1.8.0: + '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - dev: true - - /@nodelib/fs.stat@2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - dev: true - - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - dev: true - - /@paulmillr/qr@0.2.1: - resolution: {integrity: sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==} - deprecated: 'The package is now available as "qr": npm install qr' - dev: false - - /@polka/url@1.0.0-next.29: - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - dev: true - - /@reown/appkit-common@1.7.8(typescript@5.9.3)(zod@3.22.4): - resolution: {integrity: sha512-ridIhc/x6JOp7KbDdwGKY4zwf8/iK8EYBl+HtWrruutSLwZyVi5P8WaZa+8iajL6LcDcDF7LoyLwMTym7SRuwQ==} - dependencies: - big.js: 6.2.2 - dayjs: 1.11.13 - viem: 2.38.6(typescript@5.9.3)(zod@3.22.4) - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - zod - dev: false - - /@reown/appkit-common@1.7.8(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-ridIhc/x6JOp7KbDdwGKY4zwf8/iK8EYBl+HtWrruutSLwZyVi5P8WaZa+8iajL6LcDcDF7LoyLwMTym7SRuwQ==} - dependencies: - big.js: 6.2.2 - dayjs: 1.11.13 - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - zod - dev: false - - /@reown/appkit-controllers@1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-IdXlJlivrlj6m63VsGLsjtPHHsTWvKGVzWIP1fXZHVqmK+rZCBDjCi9j267Rb9/nYRGHWBtlFQhO8dK35WfeDA==} - dependencies: - '@reown/appkit-common': 1.7.8(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(typescript@5.9.3) - '@walletconnect/universal-provider': 2.21.0(typescript@5.9.3)(zod@3.25.76) - valtio: 1.13.2(react@18.3.1) - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@reown/appkit-pay@1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-OSGQ+QJkXx0FEEjlpQqIhT8zGJKOoHzVnyy/0QFrl3WrQTjCzg0L6+i91Ad5Iy1zb6V5JjqtfIFpRVRWN4M3pw==} - dependencies: - '@reown/appkit-common': 1.7.8(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(react@18.3.1)(typescript@5.9.3)(valtio@1.13.2)(zod@3.25.76) - lit: 3.3.0 - valtio: 1.13.2(react@18.3.1) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@reown/appkit-polyfills@1.7.8: - resolution: {integrity: sha512-W/kq786dcHHAuJ3IV2prRLEgD/2iOey4ueMHf1sIFjhhCGMynMkhsOhQMUH0tzodPqUgAC494z4bpIDYjwWXaA==} - dependencies: - buffer: 6.0.3 - dev: false - - /@reown/appkit-scaffold-ui@1.7.8(react@18.3.1)(typescript@5.9.3)(valtio@1.13.2)(zod@3.25.76): - resolution: {integrity: sha512-RCeHhAwOrIgcvHwYlNWMcIDibdI91waaoEYBGw71inE0kDB8uZbE7tE6DAXJmDkvl0qPh+DqlC4QbJLF1FVYdQ==} - dependencies: - '@reown/appkit-common': 1.7.8(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(react@18.3.1)(typescript@5.9.3)(valtio@1.13.2)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(typescript@5.9.3) - lit: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - valtio - - zod - dev: false - - /@reown/appkit-ui@1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-1hjCKjf6FLMFzrulhl0Y9Vb9Fu4royE+SXCPSWh4VhZhWqlzUFc7kutnZKx8XZFVQH4pbBvY62SpRC93gqoHow==} - dependencies: - '@reown/appkit-common': 1.7.8(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(typescript@5.9.3) - lit: 3.3.0 - qrcode: 1.5.3 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@reown/appkit-utils@1.7.8(react@18.3.1)(typescript@5.9.3)(valtio@1.13.2)(zod@3.25.76): - resolution: {integrity: sha512-8X7UvmE8GiaoitCwNoB86pttHgQtzy4ryHZM9kQpvjQ0ULpiER44t1qpVLXNM4X35O0v18W0Dk60DnYRMH2WRw==} - peerDependencies: - valtio: 1.13.2 - dependencies: - '@reown/appkit-common': 1.7.8(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-wallet': 1.7.8(typescript@5.9.3) - '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(typescript@5.9.3)(zod@3.25.76) - valtio: 1.13.2(react@18.3.1) - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@reown/appkit-wallet@1.7.8(typescript@5.9.3): - resolution: {integrity: sha512-kspz32EwHIOT/eg/ZQbFPxgXq0B/olDOj3YMu7gvLEFz4xyOFd/wgzxxAXkp5LbG4Cp++s/elh79rVNmVFdB9A==} - dependencies: - '@reown/appkit-common': 1.7.8(typescript@5.9.3)(zod@3.22.4) - '@reown/appkit-polyfills': 1.7.8 - '@walletconnect/logger': 2.1.2 - zod: 3.22.4 - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - dev: false - - /@reown/appkit@1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==} - dependencies: - '@reown/appkit-common': 1.7.8(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-pay': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(react@18.3.1)(typescript@5.9.3)(valtio@1.13.2)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(react@18.3.1)(typescript@5.9.3)(valtio@1.13.2)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(typescript@5.9.3) - '@walletconnect/types': 2.21.0 - '@walletconnect/universal-provider': 2.21.0(typescript@5.9.3)(zod@3.25.76) - bs58: 6.0.0 - valtio: 1.13.2(react@18.3.1) - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false + '@openfacilitator/sdk@1.0.0': + resolution: {integrity: sha512-9pqY2hv6EGoLZhCrce4fb0KBHpGcd1EKVbTSFLCdPCVuCBNMvlsYsliFG1knnCRrr7cOIvEJ6YeBWBh2b9xD+w==} + engines: {node: '>=18'} - /@rollup/rollup-android-arm-eabi@4.53.2: - resolution: {integrity: sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==} + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-android-arm64@4.53.2: - resolution: {integrity: sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==} + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-arm64@4.53.2: - resolution: {integrity: sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==} + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-x64@4.53.2: - resolution: {integrity: sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==} + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-freebsd-arm64@4.53.2: - resolution: {integrity: sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==} + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-freebsd-x64@4.53.2: - resolution: {integrity: sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==} + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} cpu: [x64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.53.2: - resolution: {integrity: sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm-musleabihf@4.53.2: - resolution: {integrity: sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==} + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-gnu@4.53.2: - resolution: {integrity: sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==} + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-musl@4.53.2: - resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-loong64-gnu@4.53.2: - resolution: {integrity: sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==} + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} cpu: [loong64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-ppc64-gnu@4.53.2: - resolution: {integrity: sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==} + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-riscv64-gnu@4.53.2: - resolution: {integrity: sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==} + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-riscv64-musl@4.53.2: - resolution: {integrity: sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==} + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-s390x-gnu@4.53.2: - resolution: {integrity: sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==} + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-gnu@4.53.2: - resolution: {integrity: sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==} + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-musl@4.53.2: - resolution: {integrity: sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==} + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-openharmony-arm64@4.53.2: - resolution: {integrity: sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==} + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} cpu: [arm64] os: [openharmony] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-arm64-msvc@4.53.2: - resolution: {integrity: sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==} + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-ia32-msvc@4.53.2: - resolution: {integrity: sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==} + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-x64-gnu@4.53.2: - resolution: {integrity: sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==} + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-x64-msvc@4.53.2: - resolution: {integrity: sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==} + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - - /@safe-global/safe-apps-provider@0.18.6(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==} - dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(typescript@5.9.3)(zod@3.25.76) - events: 3.3.0 - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - zod - dev: false - - /@safe-global/safe-apps-sdk@9.1.0(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q==} - dependencies: - '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - zod - dev: false - /@safe-global/safe-gateway-typescript-sdk@3.23.1: - resolution: {integrity: sha512-6ORQfwtEJYpalCeVO21L4XXGSdbEMfyp2hEv6cP82afKXSwvse6d3sdelgaPWUxHIsFRkWvHDdzh8IyyKHZKxw==} - engines: {node: '>=16'} - dev: false + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - /@scure/base@1.1.9: - resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} - dev: false + '@solana/buffer-layout@4.0.1': + resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} + engines: {node: '>=5.10'} - /@scure/base@1.2.6: - resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} - dev: false - - /@scure/bip32@1.4.0: - resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} - dependencies: - '@noble/curves': 1.4.2 - '@noble/hashes': 1.4.0 - '@scure/base': 1.1.9 - dev: false - - /@scure/bip32@1.6.2: - resolution: {integrity: sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==} - dependencies: - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 - '@scure/base': 1.2.6 - dev: false - - /@scure/bip32@1.7.0: - resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} - dependencies: - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - dev: false - - /@scure/bip39@1.3.0: - resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} - dependencies: - '@noble/hashes': 1.4.0 - '@scure/base': 1.1.9 - dev: false - - /@scure/bip39@1.5.4: - resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==} - dependencies: - '@noble/hashes': 1.7.1 - '@scure/base': 1.2.6 - dev: false - - /@scure/bip39@1.6.0: - resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} - dependencies: - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - dev: false - - /@socket.io/component-emitter@3.1.2: - resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - dev: false - - /@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0): - resolution: {integrity: sha512-qPKxdxaEsFxebZ4K5RPuy7VQIm/tfJLa1+Nlt3KNA8EYQkz9Xm8htdoEaXVrer9kpgzzp9R3I3Bh6omwCM06tQ==} - peerDependencies: - '@solana/kit': ^2.1.0 - dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - dev: false - - /@solana-program/system@0.8.1(@solana/kit@3.0.3): - resolution: {integrity: sha512-71U9Mzdpw8HQtfgfJSL5xKZbLMRnza2Llsfk7gGnmg2waqK+o8MMH4YNma8xXS1UmOBptXIiNvoZ3p7cmOVktg==} - peerDependencies: - '@solana/kit': ^3.0 - dependencies: - '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - dev: false - - /@solana-program/token-2022@0.4.2(@solana/kit@2.3.0)(@solana/sysvars@2.3.0): - resolution: {integrity: sha512-zIpR5t4s9qEU3hZKupzIBxJ6nUV5/UVyIT400tu9vT1HMs5JHxaTTsb5GUhYjiiTvNwU0MQavbwc4Dl29L0Xvw==} - peerDependencies: - '@solana/kit': ^2.1.0 - '@solana/sysvars': ^2.1.0 - dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - dev: false - - /@solana-program/token@0.5.1(@solana/kit@2.3.0): - resolution: {integrity: sha512-bJvynW5q9SFuVOZ5vqGVkmaPGA0MCC+m9jgJj1nk5m20I389/ms69ASnhWGoOPNcie7S9OwBX0gTj2fiyWpfag==} - peerDependencies: - '@solana/kit': ^2.1.0 - dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - dev: false - - /@solana-program/token@0.6.0(@solana/kit@3.0.3): - resolution: {integrity: sha512-omkZh4Tt9rre4wzWHNOhOEHyenXQku3xyc/UrKvShexA/Qlhza67q7uRwmwEDUs4QqoDBidSZPooOmepnA/jig==} - peerDependencies: - '@solana/kit': ^3.0 - dependencies: - '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - dev: false - - /@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-QgQTj404Z6PXNOyzaOpSzjgMOuGwG8vC66jSDB+3zHaRcEPRVRd2sVSrd1U6sHtnV3aiaS6YyDuPQMheg4K2jw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec': 2.3.0(typescript@5.9.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/accounts@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-KqlePrlZaHXfu8YQTCxN204ZuVm9o68CCcUr6l27MG2cuRUtEM1Ta0iR8JFkRUAEfZJC4Cu0ZDjK/v49loXjZQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec': 3.0.3(typescript@5.9.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/addresses@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-ypTNkY2ZaRFpHLnHAgaW8a83N0/WoqdFvCqf4CQmnMdFsZSdC7qOwcbd7YzdaQn9dy+P2hybewzB+KP7LutxGA==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/assertions': 2.3.0(typescript@5.9.3) - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/nominal-types': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/addresses@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-AuMwKhJI89ANqiuJ/fawcwxNKkSeHH9CApZd2xelQQLS7X8uxAOovpcmEgiObQuiVP944s9ScGUT62Bdul9qYg==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/assertions': 3.0.3(typescript@5.9.3) - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/nominal-types': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/assertions@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-Ekoet3khNg3XFLN7MIz8W31wPQISpKUGDGTylLptI+JjCDWx3PIa88xjEMqFo02WJ8sBj2NLV64Xg1sBcsHjZQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/assertions@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-2qspxdbWp2y62dfCIlqeWQr4g+hE8FYSSwcaP6itwMwGRb8393yDGCJfI/znuzJh6m/XVWhMHIgFgsBwnevCmg==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/buffer-layout-utils@0.2.0(typescript@5.9.3): - resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} - engines: {node: '>= 10'} - dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/web3.js': 1.98.4(typescript@5.9.3) - bigint-buffer: 1.1.5 - bignumber.js: 9.3.1 - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate - dev: false - - /@solana/buffer-layout@4.0.1: - resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} - engines: {node: '>=5.10'} - dependencies: - buffer: 6.0.3 - - /@solana/codecs-core@2.0.0-rc.1(typescript@5.9.3): - resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/errors': 2.0.0-rc.1(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/codecs-core@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - - /@solana/codecs-core@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-emKykJ3h1DmnDOY29Uv9eJXP8E/FHzvlUBJ6te+5EbKdFjj7vdlKYPfDxOI6iGdXTY+YC/ELtbNBh6QwF2uEDQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.9.3): - resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/codecs-data-structures@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-qvU5LE5DqEdYMYgELRHv+HMOx73sSoV1ZZkwIrclwUmwTbTaH8QAJURBj0RhQ/zCne7VuLLOZFFGv6jGigWhSw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/codecs-data-structures@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-R15cLp8riJvToXziW8lP6AMSwsztGhEnwgyGmll32Mo0Yjq+hduW2/fJrA/TJs6tA/OgTzMQjlxgk009EqZHCw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-numbers': 3.0.3(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/codecs-numbers@2.0.0-rc.1(typescript@5.9.3): - resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/codecs-numbers@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - - /@solana/codecs-numbers@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-pfXkH9J0glrM8qj6389GAn30+cJOxzXLR2FsPOHCUMXrqLhGjMMZAWhsQkpOQ37SGc/7EiQsT/gmyGC7gxHqJQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.9.3) - fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 5.9.3 - dev: false - - /@solana/codecs-strings@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-y5pSBYwzVziXu521hh+VxqUtp0hYGTl1eWGoc1W+8mdvBdC1kTqm/X7aYQw33J42hw03JjryvYOvmGgk3Qz/Ug==} - engines: {node: '>=20.18.0'} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 5.9.3 - dev: false - - /@solana/codecs-strings@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-VHBXnnTVtcQ1j+7Vrz+qSYo38no+jiHRdGnhFspRXEHNJbllzwKqgBE7YN3qoIXH+MKxgJUcwO5KHmdzf8Wn2A==} - engines: {node: '>=20.18.0'} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-numbers': 3.0.3(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 5.9.3 - dev: false - - /@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.9.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.3) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/codecs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-JVqGPkzoeyU262hJGdH64kNLH0M+Oew2CIPOa/9tR3++q2pEd4jU2Rxdfye9sd0Ce3XJrR5AIa8ZfbyQXzjh+g==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-data-structures': 2.3.0(typescript@5.9.3) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/options': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/codecs@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-GOHwTlIQsCoJx9Ryr6cEf0FHKAQ7pY4aO4xgncAftrv0lveTQ1rPP2inQ1QT0gJllsIa8nwbfXAADs9nNJxQDA==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-data-structures': 3.0.3(typescript@5.9.3) - '@solana/codecs-numbers': 3.0.3(typescript@5.9.3) - '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/options': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/errors@2.0.0-rc.1(typescript@5.9.3): - resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} - hasBin: true - peerDependencies: - typescript: '>=5' - dependencies: - chalk: 5.6.2 - commander: 12.1.0 - typescript: 5.9.3 - dev: false - - /@solana/errors@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==} - engines: {node: '>=20.18.0'} - hasBin: true - peerDependencies: - typescript: '>=5.3.3' - dependencies: - chalk: 5.6.2 - commander: 14.0.2 - typescript: 5.9.3 - - /@solana/errors@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-1l84xJlHNva6io62PcYfUamwWlc0eM95nHgCrKX0g0cLoC6D6QHYPCEbEVkR+C5UtP9JDgyQM8MFiv+Ei5tO9Q==} - engines: {node: '>=20.18.0'} - hasBin: true - peerDependencies: - typescript: '>=5.3.3' - dependencies: - chalk: 5.6.2 - commander: 14.0.0 - typescript: 5.9.3 - dev: false - - /@solana/fast-stable-stringify@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-KfJPrMEieUg6D3hfQACoPy0ukrAV8Kio883llt/8chPEG3FVTX9z/Zuf4O01a15xZmBbmQ7toil2Dp0sxMJSxw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/fast-stable-stringify@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-ED0pxB6lSEYvg+vOd5hcuQrgzEDnOrURFgp1ZOY+lQhJkQU6xo+P829NcJZQVP1rdU2/YQPAKJKEseyfe9VMIw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/functional@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-AgsPh3W3tE+nK3eEw/W9qiSfTGwLYEvl0rWaxHht/lRcuDVwfKRzeSa5G79eioWFFqr+pTtoCr3D3OLkwKz02Q==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/functional@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-2qX1kKANn8995vOOh5S9AmF4ItGZcfbny0w28Eqy8AFh+GMnSDN4gqpmV2LvxBI9HibXZptGH3RVOMk82h1Mpw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/instruction-plans@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-eqoaPtWtmLTTpdvbt4BZF5H6FIlJtXi9H7qLOM1dLYonkOX2Ncezx5NDCZ9tMb2qxVMF4IocYsQnNSnMfjQF1w==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/instructions': 3.0.3(typescript@5.9.3) - '@solana/promises': 3.0.3(typescript@5.9.3) - '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/instructions@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-PLMsmaIKu7hEAzyElrk2T7JJx4D+9eRwebhFZpy2PXziNSmFF929eRHKUsKqBFM3cYR1Yy3m6roBZfA+bGE/oQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/instructions@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-4csIi8YUDb5j/J+gDzmYtOvq7ZWLbCxj4t0xKn+fPrBk/FD2pK29KVT3Fu7j4Lh1/ojunQUP9X4NHwUexY3PnA==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/keys@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-ZVVdga79pNH+2pVcm6fr2sWz9HTwfopDVhYb0Lh3dh+WBmJjwkabXEIHey2rUES7NjFa/G7sV8lrUn/v8LDCCQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/assertions': 2.3.0(typescript@5.9.3) - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/nominal-types': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/keys@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-tp8oK9tMadtSIc4vF4aXXWkPd4oU5XPW8nf28NgrGDWGt25fUHIydKjkf2hPtMt9i1WfRyQZ33B5P3dnsNqcPQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/assertions': 3.0.3(typescript@5.9.3) - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/nominal-types': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-sb6PgwoW2LjE5oTFu4lhlS/cGt/NB3YrShEyx7JgWFWysfgLdJnhwWThgwy/4HjNsmtMrQGWVls0yVBHcMvlMQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/functional': 2.3.0(typescript@5.9.3) - '@solana/instructions': 2.3.0(typescript@5.9.3) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - dev: false - - /@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-CEEhCDmkvztd1zbgADsEQhmj9GyWOOGeW1hZD+gtwbBSF5YN1uofS/pex5MIh/VIqKRj+A2UnYWI1V+9+q/lyQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/accounts': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/functional': 3.0.3(typescript@5.9.3) - '@solana/instruction-plans': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/instructions': 3.0.3(typescript@5.9.3) - '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/programs': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-parsed-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/signers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/sysvars': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - dev: false - - /@solana/nominal-types@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-uKlMnlP4PWW5UTXlhKM8lcgIaNj8dvd8xO4Y9l+FVvh9RvW2TO0GwUO6JCo7JBzCB0PSqRJdWWaQ8pu1Ti/OkA==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/nominal-types@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-aZavCiexeUAoMHRQg4s1AHkH3wscbOb70diyfjhwZVgFz1uUsFez7csPp9tNFkNolnadVb2gky7yBk3IImQJ6A==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.3) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.9.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.3) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/options@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-PPnnZBRCWWoZQ11exPxf//DRzN2C6AoFsDI/u2AsQfYih434/7Kp4XLpfOMT/XESi+gdBMFNNfbES5zg3wAIkw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-data-structures': 2.3.0(typescript@5.9.3) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/options@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-jarsmnQ63RN0JPC5j9sgUat07NrL9PC71XU7pUItd6LOHtu4+wJMio3l5mT0DHVfkfbFLL6iI6+QmXSVhTNF3g==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-data-structures': 3.0.3(typescript@5.9.3) - '@solana/codecs-numbers': 3.0.3(typescript@5.9.3) - '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/programs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-UXKujV71VCI5uPs+cFdwxybtHZAIZyQkqDiDnmK+DawtOO9mBn4Nimdb/6RjR2CXT78mzO9ZCZ3qfyX+ydcB7w==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/programs@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-JZlVE3/AeSNDuH3aEzCZoDu8GTXkMpGXxf93zXLzbxfxhiQ/kHrReN4XE/JWZ/uGWbaFZGR5B3UtdN2QsoZL7w==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/promises@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-GjVgutZKXVuojd9rWy1PuLnfcRfqsaCm7InCiZc8bqmJpoghlyluweNc7ml9Y5yQn1P2IOyzh9+p/77vIyNybQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/promises@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-K+UflGBVxj30XQMHTylHHZJdKH5QG3oj5k2s42GrZ/Wbu72oapVJySMBgpK45+p90t8/LEqV6rRPyTXlet9J+Q==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/rpc-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-UUdiRfWoyYhJL9PPvFeJr4aJ554ob2jXcpn4vKmRVn9ire0sCbpQKYx6K8eEKHZWXKrDW8IDspgTl0gT/aJWVg==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec': 2.3.0(typescript@5.9.3) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/rpc-api@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-Yym9/Ama62OY69rAZgbOCAy1QlqaWAyb0VlqFuwSaZV1pkFCCFSwWEJEsiN1n8pb2ZP+RtwNvmYixvWizx9yvA==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-parsed-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec': 3.0.3(typescript@5.9.3) - '@solana/rpc-transformers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/rpc-parsed-types@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-B5pHzyEIbBJf9KHej+zdr5ZNAdSvu7WLU2lOUPh81KHdHQs6dEb310LGxcpCc7HVE8IEdO20AbckewDiAN6OCg==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/rpc-parsed-types@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-/koM05IM2fU91kYDQxXil3VBNlOfcP+gXE0js1sdGz8KonGuLsF61CiKB5xt6u1KEXhRyDdXYLjf63JarL4Ozg==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/rpc-spec-types@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-xQsb65lahjr8Wc9dMtP7xa0ZmDS8dOE2ncYjlvfyw/h4mpdXTUdrSMi6RtFwX33/rGuztQ7Hwaid5xLNSLvsFQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/rpc-spec-types@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-A6Jt8SRRetnN3CeGAvGJxigA9zYRslGgWcSjueAZGvPX+MesFxEUjSWZCfl+FogVFvwkqfkgQZQbPAGZQFJQ6Q==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - typescript: 5.9.3 - dev: false - - /@solana/rpc-spec@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-fA2LMX4BMixCrNB2n6T83AvjZ3oUQTu7qyPLyt8gHQaoEAXs8k6GZmu6iYcr+FboQCjUmRPgMaABbcr9j2J9Sw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/rpc-spec@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-MZn5/8BebB6MQ4Gstw6zyfWsFAZYAyLzMK+AUf/rSfT8tPmWiJ/mcxnxqOXvFup/l6D67U8pyGpIoFqwCeZqqA==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec-types': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/rpc-subscriptions-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-9mCjVbum2Hg9KGX3LKsrI5Xs0KX390lS+Z8qB80bxhar6MJPugqIPH8uRgLhCW9GN3JprAfjRNl7our8CPvsPQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/rpc-subscriptions-api@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-MGgVK3PUS15qsjuhimpzGZrKD/CTTvS0mAlQ0Jw84zsr1RJVdQJK/F0igu07BVd172eTZL8d90NoAQ3dahW5pA==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-spec': 3.0.3(typescript@5.9.3) - '@solana/rpc-transformers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-2oL6ceFwejIgeWzbNiUHI2tZZnaOxNTSerszcin7wYQwijxtpVgUHiuItM/Y70DQmH9sKhmikQp+dqeGalaJxw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - ws: ^8.18.0 - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/functional': 2.3.0(typescript@5.9.3) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) - '@solana/subscribable': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - dev: false - - /@solana/rpc-subscriptions-channel-websocket@3.0.3(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-zUzUlb8Cwnw+SHlsLrSqyBRtOJKGc+FvSNJo/vWAkLShoV0wUDMPv7VvhTngJx3B/3ANfrOZ4i08i9QfYPAvpQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - ws: ^8.18.0 - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/functional': 3.0.3(typescript@5.9.3) - '@solana/rpc-subscriptions-spec': 3.0.3(typescript@5.9.3) - '@solana/subscribable': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - dev: false - - /@solana/rpc-subscriptions-spec@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-rdmVcl4PvNKQeA2l8DorIeALCgJEMSu7U8AXJS1PICeb2lQuMeaR+6cs/iowjvIB0lMVjYN2sFf6Q3dJPu6wWg==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/promises': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - '@solana/subscribable': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/rpc-subscriptions-spec@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-9KpQ32OBJWS85mn6q3gkM0AjQe1LKYlMU7gpJRrla/lvXxNLhI95tz5K6StctpUreVmRWTVkNamHE69uUQyY8A==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/promises': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec-types': 3.0.3(typescript@5.9.3) - '@solana/subscribable': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-Uyr10nZKGVzvCOqwCZgwYrzuoDyUdwtgQRefh13pXIrdo4wYjVmoLykH49Omt6abwStB0a4UL5gX9V4mFdDJZg==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.3) - '@solana/functional': 2.3.0(typescript@5.9.3) - '@solana/promises': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.3)(ws@8.18.3) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/subscribable': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - dev: false - - /@solana/rpc-subscriptions@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-LRvz6NaqvtsYFd32KwZ+rwYQ9XCs+DWjV8BvBLsJpt9/NWSuHf/7Sy/vvP6qtKxut692H/TMvHnC4iulg0WmiQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/fast-stable-stringify': 3.0.3(typescript@5.9.3) - '@solana/functional': 3.0.3(typescript@5.9.3) - '@solana/promises': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-subscriptions-api': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 3.0.3(typescript@5.9.3)(ws@8.18.3) - '@solana/rpc-subscriptions-spec': 3.0.3(typescript@5.9.3) - '@solana/rpc-transformers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/subscribable': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - dev: false - - /@solana/rpc-transformers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-UuHYK3XEpo9nMXdjyGKkPCOr7WsZsxs7zLYDO1A5ELH3P3JoehvrDegYRAGzBS2VKsfApZ86ZpJToP0K3PhmMA==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/functional': 2.3.0(typescript@5.9.3) - '@solana/nominal-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/rpc-transformers@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-lzdaZM/dG3s19Tsk4mkJA5JBoS1eX9DnD7z62gkDwrwJDkDBzkAJT9aLcsYFfTmwTfIp6uU2UPgGYc97i1wezw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/functional': 3.0.3(typescript@5.9.3) - '@solana/nominal-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/rpc-transport-http@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-HFKydmxGw8nAF5N+S0NLnPBDCe5oMDtI2RAmW8DMqP4U3Zxt2XWhvV1SNkAldT5tF0U1vP+is6fHxyhk4xqEvg==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - undici-types: 7.16.0 - dev: false - - /@solana/rpc-transport-http@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-bIXFwr2LR5A97Z46dI661MJPbHnPfcShBjFzOS/8Rnr8P4ho3j/9EUtjDrsqoxGJT3SLWj5OlyXAlaDAvVTOUQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec-types': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - undici-types: 7.16.0 - dev: false - - /@solana/rpc-types@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-O09YX2hED2QUyGxrMOxQ9GzH1LlEwwZWu69QbL4oYmIf6P5dzEEHcqRY6L1LsDVqc/dzAdEs/E1FaPrcIaIIPw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/nominal-types': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/rpc-types@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-petWQ5xSny9UfmC3Qp2owyhNU0w9SyBww4+v7tSVyXMcCC9v6j/XsqTeimH1S0qQUllnv0/FY83ohFaxofmZ6Q==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-numbers': 3.0.3(typescript@5.9.3) - '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/nominal-types': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/rpc@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-ZWN76iNQAOCpYC7yKfb3UNLIMZf603JckLKOOLTHuy9MZnTN8XV6uwvDFhf42XvhglgUjGCEnbUqWtxQ9pa/pQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.3) - '@solana/functional': 2.3.0(typescript@5.9.3) - '@solana/rpc-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-spec': 2.3.0(typescript@5.9.3) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-transport-http': 2.3.0(typescript@5.9.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/rpc@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-3oukAaLK78GegkKcm6iNmRnO4mFeNz+BMvA8T56oizoBNKiRVEq/6DFzVX/LkmZ+wvD601pAB3uCdrTPcC0YKQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/fast-stable-stringify': 3.0.3(typescript@5.9.3) - '@solana/functional': 3.0.3(typescript@5.9.3) - '@solana/rpc-api': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-spec': 3.0.3(typescript@5.9.3) - '@solana/rpc-spec-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-transformers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-transport-http': 3.0.3(typescript@5.9.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/signers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-OSv6fGr/MFRx6J+ZChQMRqKNPGGmdjkqarKkRzkwmv7v8quWsIRnJT5EV8tBy3LI4DLO/A8vKiNSPzvm1TdaiQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/instructions': 2.3.0(typescript@5.9.3) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/nominal-types': 2.3.0(typescript@5.9.3) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/signers@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-UwCd/uPYTZiwd283JKVyOWLLN5sIgMBqGDyUmNU3vo9hcmXKv5ZGm/9TvwMY2z35sXWuIOcj7etxJ8OoWc/ObQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/instructions': 3.0.3(typescript@5.9.3) - '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/nominal-types': 3.0.3(typescript@5.9.3) - '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.95.3 - dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/web3.js': 1.98.4(typescript@5.9.3) - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - typescript - dev: false - - /@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.95.3 - dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/web3.js': 1.98.4(typescript@5.9.3) - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - typescript - dev: false - - /@solana/spl-token@0.4.14(@solana/web3.js@1.98.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-u09zr96UBpX4U685MnvQsNzlvw9TiY005hk1vJmJr7gMJldoPG1eYU5/wNEyOA5lkMLiR/gOi9SFD4MefOYEsA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.95.5 - dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0(typescript@5.9.3) - '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/web3.js': 1.98.4(typescript@5.9.3) - buffer: 6.0.3 - transitivePeerDependencies: - - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - dev: false - - /@solana/subscribable@2.3.0(typescript@5.9.3): - resolution: {integrity: sha512-DkgohEDbMkdTWiKAoatY02Njr56WXx9e/dKKfmne8/Ad6/2llUIrax78nCdlvZW9quXMaXPTxZvdQqo9N669Og==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/subscribable@3.0.3(typescript@5.9.3): - resolution: {integrity: sha512-FJ27LKGHLQ5GGttPvTOLQDLrrOZEgvaJhB7yYaHAhPk25+p+erBaQpjePhfkMyUbL1FQbxn1SUJmS6jUuaPjlQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/errors': 3.0.3(typescript@5.9.3) - typescript: 5.9.3 - dev: false - - /@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-LvjADZrpZ+CnhlHqfI5cmsRzX9Rpyb1Ox2dMHnbsRNzeKAMhu9w4ZBIaeTdO322zsTr509G1B+k2ABD3whvUBA==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/sysvars@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-GnHew+QeKCs2f9ow+20swEJMH4mDfJA/QhtPgOPTYQx/z69J4IieYJ7fZenSHnA//lJ45fVdNdmy1trypvPLBQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/accounts': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-UiEuiHCfAAZEKdfne/XljFNJbsKAe701UQHKXEInYzIgBjRbvaeYZlBmkkqtxwcasgBTOmEaEKT44J14N9VZDw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/promises': 2.3.0(typescript@5.9.3) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - dev: false - - /@solana/transaction-confirmation@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-dXx0OLtR95LMuARgi2dDQlL1QYmk56DOou5q9wKymmeV3JTvfDExeWXnOgjRBBq/dEfj4ugN1aZuTaS18UirFw==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/promises': 3.0.3(typescript@5.9.3) - '@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - dev: false - - /@solana/transaction-messages@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-bgqvWuy3MqKS5JdNLH649q+ngiyOu5rGS3DizSnWwYUd76RxZl1kN6CoqHSrrMzFMvis6sck/yPGG3wqrMlAww==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-data-structures': 2.3.0(typescript@5.9.3) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/functional': 2.3.0(typescript@5.9.3) - '@solana/instructions': 2.3.0(typescript@5.9.3) - '@solana/nominal-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/transaction-messages@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-s+6NWRnBhnnjFWV4x2tzBzoWa6e5LiIxIvJlWwVQBFkc8fMGY04w7jkFh0PM08t/QFKeXBEWkyBDa/TFYdkWug==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-data-structures': 3.0.3(typescript@5.9.3) - '@solana/codecs-numbers': 3.0.3(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/functional': 3.0.3(typescript@5.9.3) - '@solana/instructions': 3.0.3(typescript@5.9.3) - '@solana/nominal-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/transactions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-LnTvdi8QnrQtuEZor5Msje61sDpPstTVwKg4y81tNxDhiyomjuvnSNLAq6QsB9gIxUqbNzPZgOG9IU4I4/Uaug==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 2.3.0(typescript@5.9.3) - '@solana/codecs-data-structures': 2.3.0(typescript@5.9.3) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 2.3.0(typescript@5.9.3) - '@solana/functional': 2.3.0(typescript@5.9.3) - '@solana/instructions': 2.3.0(typescript@5.9.3) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/nominal-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/transactions@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3): - resolution: {integrity: sha512-iMX+n9j4ON7H1nKlWEbMqMOpKYC6yVGxKKmWHT1KdLRG7v+03I4DnDeFoI+Zmw56FA+7Bbne8jwwX60Q1vk/MQ==} - engines: {node: '>=20.18.0'} - peerDependencies: - typescript: '>=5.3.3' - dependencies: - '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 3.0.3(typescript@5.9.3) - '@solana/codecs-data-structures': 3.0.3(typescript@5.9.3) - '@solana/codecs-numbers': 3.0.3(typescript@5.9.3) - '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 3.0.3(typescript@5.9.3) - '@solana/functional': 3.0.3(typescript@5.9.3) - '@solana/instructions': 3.0.3(typescript@5.9.3) - '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/nominal-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false - - /@solana/web3.js@1.98.4(typescript@5.9.3): - resolution: {integrity: sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==} - dependencies: - '@babel/runtime': 7.28.4 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@solana/buffer-layout': 4.0.1 - '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) - agentkeepalive: 4.6.0 - bn.js: 5.2.2 - borsh: 0.7.0 - bs58: 4.0.1 - buffer: 6.0.3 - fast-stable-stringify: 1.0.0 - jayson: 4.2.0 - node-fetch: 2.7.0 - rpc-websockets: 9.3.1 - superstruct: 2.0.2 - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate - - /@standard-schema/spec@1.0.0: - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - dev: true - - /@swc/helpers@0.5.17: - resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} - dependencies: - tslib: 2.8.1 - - /@tanstack/query-core@5.90.7: - resolution: {integrity: sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ==} - dev: false - - /@tanstack/react-query@5.90.7(react@18.3.1): - resolution: {integrity: sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ==} - peerDependencies: - react: ^18 || ^19 - dependencies: - '@tanstack/query-core': 5.90.7 - react: 18.3.1 - dev: false - - /@types/chai@5.2.3: - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 - dev: true - - /@types/connect@3.4.38: - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - dependencies: - '@types/node': 20.19.24 - - /@types/debug@4.1.12: - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - dependencies: - '@types/ms': 2.1.0 - dev: false - - /@types/deep-eql@4.0.2: - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - dev: true - - /@types/estree@1.0.8: - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - dev: true - - /@types/lodash@4.17.20: - resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} - dev: false - - /@types/ms@2.1.0: - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - dev: false - - /@types/node@12.20.55: - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - - /@types/node@20.19.24: - resolution: {integrity: sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==} - dependencies: - undici-types: 6.21.0 - - /@types/node@22.7.5: - resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} - dependencies: - undici-types: 6.19.8 - - /@types/trusted-types@2.0.7: - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - dev: false - - /@types/uuid@8.3.4: - resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - - /@types/ws@7.4.7: - resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} - dependencies: - '@types/node': 20.19.24 - - /@types/ws@8.18.1: - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - dependencies: - '@types/node': 20.19.24 - - /@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0)(eslint@8.57.1)(typescript@5.9.3): - resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 8.57.1 - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 1.4.3(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3): - resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.3 - eslint: 8.57.1 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/scope-manager@7.18.0: - resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} - engines: {node: ^18.18.0 || >=20.0.0} - dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - dev: true - - /@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.9.3): - resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.3) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.9.3) - debug: 4.4.3 - eslint: 8.57.1 - ts-api-utils: 1.4.3(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/types@7.18.0: - resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} - engines: {node: ^18.18.0 || >=20.0.0} - dev: true - - /@typescript-eslint/typescript-estree@7.18.0(typescript@5.9.3): - resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.3 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 - ts-api-utils: 1.4.3(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.9.3): - resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.3) - eslint: 8.57.1 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/visitor-keys@7.18.0: - resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} - engines: {node: ^18.18.0 || >=20.0.0} - dependencies: - '@typescript-eslint/types': 7.18.0 - eslint-visitor-keys: 3.4.3 - dev: true - - /@ungap/structured-clone@1.3.0: - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - dev: true - - /@vitest/expect@4.0.8: - resolution: {integrity: sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==} - dependencies: - '@standard-schema/spec': 1.0.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.0.8 - '@vitest/utils': 4.0.8 - chai: 6.2.1 - tinyrainbow: 3.0.3 - dev: true - - /@vitest/mocker@4.0.8(vite@7.2.2): - resolution: {integrity: sha512-9FRM3MZCedXH3+pIh+ME5Up2NBBHDq0wqwhOKkN4VnvCiKbVxddqH9mSGPZeawjd12pCOGnl+lo/ZGHt0/dQSg==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - dependencies: - '@vitest/spy': 4.0.8 - estree-walker: 3.0.3 - magic-string: 0.30.21 - vite: 7.2.2(@types/node@20.19.24)(tsx@4.20.6) - dev: true - - /@vitest/pretty-format@4.0.8: - resolution: {integrity: sha512-qRrjdRkINi9DaZHAimV+8ia9Gq6LeGz2CgIEmMLz3sBDYV53EsnLZbJMR1q84z1HZCMsf7s0orDgZn7ScXsZKg==} - dependencies: - tinyrainbow: 3.0.3 - dev: true - - /@vitest/runner@4.0.8: - resolution: {integrity: sha512-mdY8Sf1gsM8hKJUQfiPT3pn1n8RF4QBcJYFslgWh41JTfrK1cbqY8whpGCFzBl45LN028g0njLCYm0d7XxSaQQ==} - dependencies: - '@vitest/utils': 4.0.8 - pathe: 2.0.3 - dev: true - - /@vitest/snapshot@4.0.8: - resolution: {integrity: sha512-Nar9OTU03KGiubrIOFhcfHg8FYaRaNT+bh5VUlNz8stFhCZPNrJvmZkhsr1jtaYvuefYFwK2Hwrq026u4uPWCw==} - dependencies: - '@vitest/pretty-format': 4.0.8 - magic-string: 0.30.21 - pathe: 2.0.3 - dev: true - - /@vitest/spy@4.0.8: - resolution: {integrity: sha512-nvGVqUunyCgZH7kmo+Ord4WgZ7lN0sOULYXUOYuHr55dvg9YvMz3izfB189Pgp28w0vWFbEEfNc/c3VTrqrXeA==} - dev: true - - /@vitest/ui@4.0.8(vitest@4.0.8): - resolution: {integrity: sha512-F9jI5rSstNknPlTlPN2gcc4gpbaagowuRzw/OJzl368dvPun668Q182S8Q8P9PITgGCl5LAKXpzuue106eM4wA==} - peerDependencies: - vitest: 4.0.8 - dependencies: - '@vitest/utils': 4.0.8 - fflate: 0.8.2 - flatted: 3.3.3 - pathe: 2.0.3 - sirv: 3.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vitest: 4.0.8(@types/node@20.19.24)(@vitest/ui@4.0.8)(tsx@4.20.6) - dev: true - - /@vitest/utils@4.0.8: - resolution: {integrity: sha512-pdk2phO5NDvEFfUTxcTP8RFYjVj/kfLSPIN5ebP2Mu9kcIMeAQTbknqcFEyBcC4z2pJlJI9aS5UQjcYfhmKAow==} - dependencies: - '@vitest/pretty-format': 4.0.8 - tinyrainbow: 3.0.3 - dev: true - - /@wagmi/connectors@6.1.3(@tanstack/react-query@5.90.7)(@wagmi/core@2.22.1)(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.38.6)(wagmi@2.19.2)(ws@8.18.3)(zod@3.25.76): - resolution: {integrity: sha512-Rx3/4Rug3SrBC/WSuNUC0XEpWC/yP71BhQzz3u064ueemIWtvrIxXZxH2byWd0dF3osarjuHBr904bm3L6eNHg==} - peerDependencies: - '@wagmi/core': 2.22.1 - typescript: '>=5.0.4' - viem: 2.x - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@base-org/account': 2.4.0(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(ws@8.18.3)(zod@3.25.76) - '@coinbase/wallet-sdk': 4.3.6(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(zod@3.25.76) - '@gemini-wallet/core': 0.3.1(viem@2.38.6) - '@metamask/sdk': 0.33.1 - '@safe-global/safe-apps-provider': 0.18.6(typescript@5.9.3)(zod@3.25.76) - '@safe-global/safe-apps-sdk': 9.1.0(typescript@5.9.3)(zod@3.25.76) - '@wagmi/core': 2.22.1(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.38.6) - '@walletconnect/ethereum-provider': 2.21.1(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - cbw-sdk: /@coinbase/wallet-sdk@3.9.3 - porto: 0.2.35(@tanstack/react-query@5.90.7)(@wagmi/core@2.22.1)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.38.6)(wagmi@2.19.2) - typescript: 5.9.3 - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@tanstack/react-query' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - debug - - encoding - - expo-auth-session - - expo-crypto - - expo-web-browser - - fastestsmallesttextencoderdecoder - - immer - - ioredis - - react - - react-native - - supports-color - - uploadthing - - use-sync-external-store - - utf-8-validate - - wagmi - - ws - - zod - dev: false - - /@wagmi/core@2.22.1(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.38.6): - resolution: {integrity: sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==} - peerDependencies: - '@tanstack/query-core': '>=5.0.0' - typescript: '>=5.0.4' - viem: 2.x - peerDependenciesMeta: - '@tanstack/query-core': - optional: true - typescript: - optional: true - dependencies: - eventemitter3: 5.0.1 - mipd: 0.0.7(typescript@5.9.3) - typescript: 5.9.3 - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - zustand: 5.0.0(react@18.3.1)(use-sync-external-store@1.4.0) - transitivePeerDependencies: - - '@types/react' - - immer - - react - - use-sync-external-store - dev: false - - /@walletconnect/core@2.21.0(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-o6R7Ua4myxR8aRUAJ1z3gT9nM+jd2B2mfamu6arzy1Cc6vi10fIwFWb6vg3bC8xJ6o9H3n/cN5TOW3aA9Y1XVw==} - engines: {node: '>=18'} - dependencies: - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/relay-api': 1.0.11 - '@walletconnect/relay-auth': 1.1.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(typescript@5.9.3)(zod@3.25.76) - '@walletconnect/window-getters': 1.0.1 - es-toolkit: 1.33.0 - events: 3.3.0 - uint8arrays: 3.1.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@walletconnect/core@2.21.1(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-Tp4MHJYcdWD846PH//2r+Mu4wz1/ZU/fr9av1UWFiaYQ2t2TPLDiZxjLw54AAEpMqlEHemwCgiRiAmjR1NDdTQ==} - engines: {node: '>=18'} - dependencies: - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/relay-api': 1.0.11 - '@walletconnect/relay-auth': 1.1.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(typescript@5.9.3)(zod@3.25.76) - '@walletconnect/window-getters': 1.0.1 - es-toolkit: 1.33.0 - events: 3.3.0 - uint8arrays: 3.1.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@walletconnect/environment@1.0.1: - resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==} - dependencies: - tslib: 1.14.1 - dev: false - - /@walletconnect/ethereum-provider@2.21.1(react@18.3.1)(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-SSlIG6QEVxClgl1s0LMk4xr2wg4eT3Zn/Hb81IocyqNSGfXpjtawWxKxiC5/9Z95f1INyBD6MctJbL/R1oBwIw==} - deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' - dependencies: - '@reown/appkit': 1.7.8(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) - '@walletconnect/jsonrpc-http-connection': 1.0.8 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.21.1(typescript@5.9.3)(zod@3.25.76) - '@walletconnect/types': 2.21.1 - '@walletconnect/universal-provider': 2.21.1(typescript@5.9.3)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(typescript@5.9.3)(zod@3.25.76) - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@walletconnect/events@1.0.1: - resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} - dependencies: - keyvaluestorage-interface: 1.0.0 - tslib: 1.14.1 - dev: false - - /@walletconnect/heartbeat@1.2.2: - resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==} - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/time': 1.0.2 - events: 3.3.0 - dev: false - - /@walletconnect/jsonrpc-http-connection@1.0.8: - resolution: {integrity: sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==} - dependencies: - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/safe-json': 1.0.2 - cross-fetch: 3.2.0 - events: 3.3.0 - transitivePeerDependencies: - - encoding - dev: false - - /@walletconnect/jsonrpc-provider@1.0.14: - resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==} - dependencies: - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/safe-json': 1.0.2 - events: 3.3.0 - dev: false - - /@walletconnect/jsonrpc-types@1.0.4: - resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==} - dependencies: - events: 3.3.0 - keyvaluestorage-interface: 1.0.0 - dev: false - - /@walletconnect/jsonrpc-utils@1.0.8: - resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==} - dependencies: - '@walletconnect/environment': 1.0.1 - '@walletconnect/jsonrpc-types': 1.0.4 - tslib: 1.14.1 - dev: false - - /@walletconnect/jsonrpc-ws-connection@1.0.16: - resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==} - dependencies: - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/safe-json': 1.0.2 - events: 3.3.0 - ws: 7.5.10 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: false - - /@walletconnect/keyvaluestorage@1.1.1: - resolution: {integrity: sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==} - peerDependencies: - '@react-native-async-storage/async-storage': 1.x - peerDependenciesMeta: - '@react-native-async-storage/async-storage': - optional: true - dependencies: - '@walletconnect/safe-json': 1.0.2 - idb-keyval: 6.2.2 - unstorage: 1.17.2(idb-keyval@6.2.2) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - db0 - - ioredis - - uploadthing - dev: false - - /@walletconnect/logger@2.1.2: - resolution: {integrity: sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==} - dependencies: - '@walletconnect/safe-json': 1.0.2 - pino: 7.11.0 - dev: false - - /@walletconnect/relay-api@1.0.11: - resolution: {integrity: sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==} - dependencies: - '@walletconnect/jsonrpc-types': 1.0.4 - dev: false - - /@walletconnect/relay-auth@1.1.0: - resolution: {integrity: sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==} - dependencies: - '@noble/curves': 1.8.0 - '@noble/hashes': 1.7.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - uint8arrays: 3.1.0 - dev: false - - /@walletconnect/safe-json@1.0.2: - resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==} - dependencies: - tslib: 1.14.1 - dev: false - - /@walletconnect/sign-client@2.21.0(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==} - deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' - dependencies: - '@walletconnect/core': 2.21.0(typescript@5.9.3)(zod@3.25.76) - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/logger': 2.1.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(typescript@5.9.3)(zod@3.25.76) - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@walletconnect/sign-client@2.21.1(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-QaXzmPsMnKGV6tc4UcdnQVNOz4zyXgarvdIQibJ4L3EmLat73r5ZVl4c0cCOcoaV7rgM9Wbphgu5E/7jNcd3Zg==} - deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' - dependencies: - '@walletconnect/core': 2.21.1(typescript@5.9.3)(zod@3.25.76) - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/logger': 2.1.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(typescript@5.9.3)(zod@3.25.76) - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@walletconnect/time@1.0.2: - resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} - dependencies: - tslib: 1.14.1 - dev: false - - /@walletconnect/types@2.21.0: - resolution: {integrity: sha512-ll+9upzqt95ZBWcfkOszXZkfnpbJJ2CmxMfGgE5GmhdxxxCcO5bGhXkI+x8OpiS555RJ/v/sXJYMSOLkmu4fFw==} - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - db0 - - ioredis - - uploadthing - dev: false - - /@walletconnect/types@2.21.1: - resolution: {integrity: sha512-UeefNadqP6IyfwWC1Yi7ux+ljbP2R66PLfDrDm8izmvlPmYlqRerJWJvYO4t0Vvr9wrG4Ko7E0c4M7FaPKT/sQ==} - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - db0 - - ioredis - - uploadthing - dev: false - - /@walletconnect/universal-provider@2.21.0(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==} - deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/jsonrpc-http-connection': 1.0.8 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(typescript@5.9.3)(zod@3.25.76) - '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(typescript@5.9.3)(zod@3.25.76) - es-toolkit: 1.33.0 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@walletconnect/universal-provider@2.21.1(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-Wjx9G8gUHVMnYfxtasC9poGm8QMiPCpXpbbLFT+iPoQskDDly8BwueWnqKs4Mx2SdIAWAwuXeZ5ojk5qQOxJJg==} - deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/jsonrpc-http-connection': 1.0.8 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(typescript@5.9.3)(zod@3.25.76) - '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(typescript@5.9.3)(zod@3.25.76) - es-toolkit: 1.33.0 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@walletconnect/utils@2.21.0(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-zfHLiUoBrQ8rP57HTPXW7rQMnYxYI4gT9yTACxVW6LhIFROTF6/ytm5SKNoIvi4a5nX5dfXG4D9XwQUCu8Ilig==} - dependencies: - '@noble/ciphers': 1.2.1 - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/relay-api': 1.0.11 - '@walletconnect/relay-auth': 1.1.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.0 - '@walletconnect/window-getters': 1.0.1 - '@walletconnect/window-metadata': 1.0.1 - bs58: 6.0.0 - detect-browser: 5.3.0 - query-string: 7.1.3 - uint8arrays: 3.1.0 - viem: 2.23.2(typescript@5.9.3)(zod@3.25.76) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@walletconnect/utils@2.21.1(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-VPZvTcrNQCkbGOjFRbC24mm/pzbRMUq2DSQoiHlhh0X1U7ZhuIrzVtAoKsrzu6rqjz0EEtGxCr3K1TGRqDG4NA==} - dependencies: - '@noble/ciphers': 1.2.1 - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/relay-api': 1.0.11 - '@walletconnect/relay-auth': 1.1.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.1 - '@walletconnect/window-getters': 1.0.1 - '@walletconnect/window-metadata': 1.0.1 - bs58: 6.0.0 - detect-browser: 5.3.0 - query-string: 7.1.3 - uint8arrays: 3.1.0 - viem: 2.23.2(typescript@5.9.3)(zod@3.25.76) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - dev: false - - /@walletconnect/window-getters@1.0.1: - resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==} - dependencies: - tslib: 1.14.1 - dev: false - - /@walletconnect/window-metadata@1.0.1: - resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} - dependencies: - '@walletconnect/window-getters': 1.0.1 - tslib: 1.14.1 - dev: false - - /abitype@1.0.6(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3 >=3.22.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - dependencies: - typescript: 5.9.3 - zod: 3.25.76 - dev: false - - /abitype@1.0.8(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3 >=3.22.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - dependencies: - typescript: 5.9.3 - zod: 3.25.76 - dev: false - - /abitype@1.1.0(typescript@5.9.3)(zod@3.22.4): - resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - dependencies: - typescript: 5.9.3 - zod: 3.22.4 - dev: false - - /abitype@1.1.0(typescript@5.9.3)(zod@4.1.12): - resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - dependencies: - typescript: 5.9.3 - zod: 4.1.12 - dev: false - - /abitype@1.1.1(typescript@5.9.3)(zod@3.22.4): - resolution: {integrity: sha512-Loe5/6tAgsBukY95eGaPSDmQHIjRZYQq8PB1MpsNccDIK8WiV+Uw6WzaIXipvaxTEL2yEB0OpEaQv3gs8pkS9Q==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - dependencies: - typescript: 5.9.3 - zod: 3.22.4 - dev: false - - /abitype@1.1.1(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-Loe5/6tAgsBukY95eGaPSDmQHIjRZYQq8PB1MpsNccDIK8WiV+Uw6WzaIXipvaxTEL2yEB0OpEaQv3gs8pkS9Q==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - dependencies: - typescript: 5.9.3 - zod: 3.25.76 - dev: false - - /abitype@1.1.1(typescript@5.9.3)(zod@4.1.12): - resolution: {integrity: sha512-Loe5/6tAgsBukY95eGaPSDmQHIjRZYQq8PB1MpsNccDIK8WiV+Uw6WzaIXipvaxTEL2yEB0OpEaQv3gs8pkS9Q==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - dependencies: - typescript: 5.9.3 - zod: 4.1.12 - dev: false - - /acorn-jsx@5.3.2(acorn@8.15.0): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.15.0 - dev: true - - /acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /aes-js@4.0.0-beta.5: - resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} - - /agentkeepalive@4.6.0: - resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} - engines: {node: '>= 8.0.0'} - dependencies: - humanize-ms: 1.2.1 - - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: true - - /ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - dev: true - - /ansi-escapes@7.2.0: - resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} - engines: {node: '>=18'} - dependencies: - environment: 1.1.0 - dev: true - - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - /ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - - /ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - dev: true - - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - dev: false - - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - dependencies: - sprintf-js: 1.0.3 - dev: true - - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true - - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - dev: true - - /assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - dev: true - - /async-mutex@0.2.6: - resolution: {integrity: sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==} - dependencies: - tslib: 2.8.1 - dev: false - - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false - - /atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - dev: false - - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - dependencies: - possible-typed-array-names: 1.1.0 - dev: false - - /axios-retry@4.5.0(axios@1.13.2): - resolution: {integrity: sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==} - peerDependencies: - axios: 0.x || 1.x - dependencies: - axios: 1.13.2 - is-retry-allowed: 2.2.0 - dev: false - - /axios@1.13.2: - resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} - dependencies: - follow-redirects: 1.15.11 - form-data: 4.0.4 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - dev: false - - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true - - /base-x@3.0.11: - resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} - dependencies: - safe-buffer: 5.2.1 - - /base-x@4.0.1: - resolution: {integrity: sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==} - dev: false - - /base-x@5.0.1: - resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} - dev: false - - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - /better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - dependencies: - is-windows: 1.0.2 - dev: true - - /big.js@6.2.2: - resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} - dev: false - - /bigint-buffer@1.1.5: - resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} - engines: {node: '>= 10.0.0'} - requiresBuild: true - dependencies: - bindings: 1.5.0 - dev: false - - /bignumber.js@9.3.1: - resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - dev: false - - /bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - dependencies: - file-uri-to-path: 1.0.0 - dev: false - - /bn.js@5.2.2: - resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} - - /borsh@0.7.0: - resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} - dependencies: - bn.js: 5.2.2 - bs58: 4.0.1 - text-encoding-utf-8: 1.0.2 - - /bowser@2.12.1: - resolution: {integrity: sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==} - dev: false - - /brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - dev: true - - /brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - dependencies: - balanced-match: 1.0.2 - dev: true - - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.1.1 - dev: true - - /bs58@4.0.1: - resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} - dependencies: - base-x: 3.0.11 - - /bs58@5.0.0: - resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} - dependencies: - base-x: 4.0.1 - dev: false - - /bs58@6.0.0: - resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} - dependencies: - base-x: 5.0.1 - dev: false - - /buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - /bufferutil@4.0.9: - resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} - engines: {node: '>=6.14.2'} - requiresBuild: true - dependencies: - node-gyp-build: 4.8.4 - - /call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - dev: false - - /call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - dev: false - - /call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - dev: false - - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - dev: true - - /camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - dev: false - - /chai@6.2.1: - resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} - engines: {node: '>=18'} - dev: true - - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - - /chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - /chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - dev: true - - /charenc@0.0.2: - resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} - dev: false - - /chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - dependencies: - readdirp: 4.1.2 - dev: false - - /ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - dev: true - - /cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} - dependencies: - restore-cursor: 5.1.0 - - /cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - dev: false - - /cli-truncate@5.1.1: - resolution: {integrity: sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==} - engines: {node: '>=20'} - dependencies: - slice-ansi: 7.1.2 - string-width: 8.1.0 - dev: true - - /cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - dev: false - - /clsx@1.2.1: - resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} - engines: {node: '>=6'} - dev: false - - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - /colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - dev: true - - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: false - - /commander@11.1.0: - resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} - engines: {node: '>=16'} - dev: false - - /commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - dev: false - - /commander@14.0.0: - resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} - engines: {node: '>=20'} - dev: false + '@solana/codecs-core@2.3.0': + resolution: {integrity: sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' - /commander@14.0.2: - resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} - engines: {node: '>=20'} + '@solana/codecs-numbers@2.3.0': + resolution: {integrity: sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + '@solana/errors@2.3.0': + resolution: {integrity: sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: '>=5.3.3' - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true + '@solana/web3.js@1.98.4': + resolution: {integrity: sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==} - /cookie-es@1.2.2: - resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} - dev: false + '@swc/helpers@0.5.21': + resolution: {integrity: sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==} - /core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: false + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - /crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - dev: false + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - /cross-fetch@3.2.0: - resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - dev: false + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - /cross-fetch@4.1.0: - resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - dev: false + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - /cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - dev: true + '@types/node@22.19.19': + resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==} - /crossws@0.3.5: - resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} - dependencies: - uncrypto: 0.1.3 - dev: false + '@types/uuid@10.0.0': + resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} - /crypt@0.0.2: - resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} - dev: false + '@types/ws@7.4.7': + resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} - /date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} - dependencies: - '@babel/runtime': 7.28.4 - dev: false + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - /dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - dev: false + '@vitest/expect@2.1.9': + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} + '@vitest/mocker@2.1.9': + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} peerDependencies: - supports-color: '*' + msw: ^2.4.9 + vite: ^5.0.0 peerDependenciesMeta: - supports-color: + msw: optional: true - dependencies: - ms: 2.1.2 - dev: false - - /debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: + vite: optional: true - dependencies: - ms: 2.1.3 - /decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - dev: false - - /decode-uri-component@0.2.2: - resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} - engines: {node: '>=0.10'} - dev: false + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true + '@vitest/runner@2.1.9': + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} - /define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - dev: false + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} - /defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} - dev: false + '@vitest/spy@2.1.9': + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} - /delay@5.0.0: - resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} - engines: {node: '>=10'} + '@vitest/utils@2.1.9': + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} - dev: false - - /derive-valtio@0.1.0(valtio@1.13.2): - resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} - peerDependencies: - valtio: '*' - dependencies: - valtio: 1.13.2(react@18.3.1) - dev: false - - /destr@2.0.5: - resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - dev: false - - /detect-browser@5.3.0: - resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} - dev: false - - /detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: true - - /dijkstrajs@1.0.3: - resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} - dev: false - - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dependencies: - path-type: 4.0.0 - dev: true - - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dependencies: - esutils: 2.0.3 - dev: true - - /dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - dev: false - - /dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - dev: false - - /duplexify@4.1.3: - resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} - dependencies: - end-of-stream: 1.4.5 - inherits: 2.0.4 - readable-stream: 3.6.2 - stream-shift: 1.0.3 - dev: false - - /eciesjs@0.4.16: - resolution: {integrity: sha512-dS5cbA9rA2VR4Ybuvhg6jvdmp46ubLn3E+px8cG/35aEDNclrqoCjg6mt0HYZ/M+OoESS3jSkCrqk1kWAEhWAw==} - engines: {bun: '>=1', deno: '>=2', node: '>=16'} - dependencies: - '@ecies/ciphers': 0.2.5(@noble/ciphers@1.3.0) - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - dev: false - - /emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: false - - /encode-utf8@1.0.3: - resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} - dev: false - - /end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - dependencies: - once: 1.4.0 - dev: false - - /engine.io-client@6.6.3: - resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.3.4 - engine.io-parser: 5.2.3 - ws: 8.17.1 - xmlhttprequest-ssl: 2.1.2 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: false - - /engine.io-parser@5.2.3: - resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} - engines: {node: '>=10.0.0'} - dev: false - - /enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - dev: true - - /environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} - dev: true - - /es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - dev: false - - /es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - dev: false - - /es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - dev: true - - /es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - dependencies: - es-errors: 1.3.0 - dev: false - - /es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - dev: false - - /es-toolkit@1.33.0: - resolution: {integrity: sha512-X13Q/ZSc+vsO1q600bvNK4bxgXMkHcf//RxCmYDaRY5DAcT+eoXjY5hoAPGMdRnWQjvyLEcyauG3b6hz76LNqg==} - dev: false - - /es6-promise@4.2.8: - resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - - /es6-promisify@5.0.0: - resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} - dependencies: - es6-promise: 4.2.8 - - /esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 - dev: true - - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true - - /eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /eslint@8.57.1: - resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) - '@eslint-community/regexpp': 4.12.2 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.1 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: true - - /espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 3.4.3 - dev: true - - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} hasBin: true - dev: true - - /esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: true - - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - dev: true - - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - dev: true - - /estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - dependencies: - '@types/estree': 1.0.8 - dev: true - - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - dev: true - - /eth-block-tracker@7.1.0: - resolution: {integrity: sha512-8YdplnuE1IK4xfqpf4iU7oBxnOYAc35934o083G8ao+8WM8QQtt/mVlAY6yIAdY1eMeLqg4Z//PZjJGmWGPMRg==} - engines: {node: '>=14.0.0'} - dependencies: - '@metamask/eth-json-rpc-provider': 1.0.1 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 5.0.2 - json-rpc-random-id: 1.0.1 - pify: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: false - - /eth-json-rpc-filters@6.0.1: - resolution: {integrity: sha512-ITJTvqoCw6OVMLs7pI8f4gG92n/St6x80ACtHodeS+IXmO0w+t1T5OOzfSt7KLSMLRkVUoexV7tztLgDxg+iig==} - engines: {node: '>=14.0.0'} - dependencies: - '@metamask/safe-event-emitter': 3.1.2 - async-mutex: 0.2.6 - eth-query: 2.1.2 - json-rpc-engine: 6.1.0 - pify: 5.0.0 - dev: false - - /eth-query@2.1.2: - resolution: {integrity: sha512-srES0ZcvwkR/wd5OQBRA1bIJMww1skfGS0s8wlwK3/oNP4+wnds60krvu5R1QbpRQjMmpG5OMIWro5s7gvDPsA==} - dependencies: - json-rpc-random-id: 1.0.1 - xtend: 4.0.2 - dev: false - - /eth-rpc-errors@4.0.3: - resolution: {integrity: sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg==} - dependencies: - fast-safe-stringify: 2.1.1 - dev: false - - /ethereum-cryptography@2.2.1: - resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} - dependencies: - '@noble/curves': 1.4.2 - '@noble/hashes': 1.4.0 - '@scure/bip32': 1.4.0 - '@scure/bip39': 1.3.0 - dev: false - - /ethers@6.15.0: - resolution: {integrity: sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ==} - engines: {node: '>=14.0.0'} - 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 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - /eventemitter2@6.4.9: - resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} - dev: false + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} - /eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - /events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - dev: false + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} - /expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} - engines: {node: '>=12.0.0'} - dev: true + base-x@3.0.11: + resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} - /extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - dev: true + base-x@5.0.1: + resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} - /extension-port-stream@3.0.0: - resolution: {integrity: sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==} - engines: {node: '>=12.0.0'} - dependencies: - readable-stream: 3.6.2 - webextension-polyfill: 0.10.0 - dev: false + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - /eyes@0.1.8: - resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} - engines: {node: '> 0.1.90'} + bn.js@5.2.3: + resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + borsh@0.7.0: + resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} - /fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - dev: true - - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true - - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true - - /fast-redact@3.5.0: - resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} - engines: {node: '>=6'} - dev: false + bs58@4.0.1: + resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} - /fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - dev: false + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} - /fast-stable-stringify@1.0.0: - resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} + buffer-layout@1.2.2: + resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==} + engines: {node: '>=4.5'} - /fastestsmallesttextencoderdecoder@1.0.22: - resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} - dev: false + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - /fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - dependencies: - reusify: 1.1.0 - dev: true + bufferutil@4.1.0: + resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} + engines: {node: '>=6.14.2'} - /fdir@6.5.0(picomatch@4.0.3): - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - dependencies: - picomatch: 4.0.3 - dev: true - - /fflate@0.8.2: - resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} - dev: true + esbuild: '>=0.18' - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flat-cache: 3.2.0 - dev: true - - /file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - dev: false - - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - dev: true - - /filter-obj@1.1.0: - resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} - engines: {node: '>=0.10.0'} - dev: false - - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - dev: true - - /flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - rimraf: 3.0.2 - dev: true - - /flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - dev: true - - /follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false - - /for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - dependencies: - is-callable: 1.2.7 - dev: false - /form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - dev: false - - /fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - dev: true - - /fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - dev: true + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true - optional: true + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - dev: false + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} - /generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} - engines: {node: '>= 0.4'} - dev: false + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: false + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - /get-east-asian-width@1.4.0: - resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} - engines: {node: '>=18'} + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} - /get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - dev: false - - /get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - dev: false + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - /get-tsconfig@4.13.0: - resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} - dependencies: - resolve-pkg-maps: 1.0.0 - dev: true + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - dev: true + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} - /glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - dependencies: - is-glob: 4.0.3 - dev: true + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - - /globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - dev: true + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + delay@5.0.0: + resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} engines: {node: '>=10'} - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - dev: true - - /gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - dev: false - - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true - - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - dev: true - - /h3@1.15.4: - resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} - dependencies: - cookie-es: 1.2.2 - crossws: 0.3.5 - defu: 6.1.4 - destr: 2.0.5 - iron-webcrypto: 1.2.1 - node-mock-http: 1.0.3 - radix3: 1.1.2 - ufo: 1.6.1 - uncrypto: 0.1.3 - dev: false - - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - dev: true - /has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - dependencies: - es-define-property: 1.0.1 - dev: false + engine.io-client@6.6.5: + resolution: {integrity: sha512-QCwxUDULPlXv8F6tqMMKx5dNkTe6OaBYRMPYeXKBlyOoKvAmE0ac6pW7fFhSscJ/5SI7666/U/B+MElbsrJlIg==} - /has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - dev: false + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} - /has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.1.0 - dev: false + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - dependencies: - function-bind: 1.1.2 - dev: false + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - /hono@4.10.4: - resolution: {integrity: sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg==} - engines: {node: '>=16.9.0'} - dev: false + es6-promisify@5.0.0: + resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} - /human-id@4.1.2: - resolution: {integrity: sha512-v/J+4Z/1eIJovEBdlV5TYj1IR+ZiohcYGRY+qN/oC9dAfKzVT023N/Bgw37hrKCoVRBvk3bqyzpr2PP5YeTMSg==} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} hasBin: true - dev: true - - /humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - dependencies: - ms: 2.1.3 - /husky@9.1.7: - resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} hasBin: true - dev: true - - /iconv-lite@0.7.0: - resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - dev: true - - /idb-keyval@6.2.1: - resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} - dev: false - - /idb-keyval@6.2.2: - resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} - dev: false - - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - /ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - dev: true - /import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - dev: true - - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true - - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: true + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - /iron-webcrypto@1.2.1: - resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} - dev: false + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - /is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - dev: false + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} - /is-buffer@1.1.6: - resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} - dev: false + eyes@0.1.8: + resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} + engines: {node: '> 0.1.90'} - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: false + fast-stable-stringify@1.0.0: + resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: false + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} - /is-fullwidth-code-point@5.1.0: - resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} - engines: {node: '>=18'} - dependencies: - get-east-asian-width: 1.4.0 - dev: true + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] - /is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} - engines: {node: '>= 0.4'} - dependencies: - call-bound: 1.0.4 - generator-function: 2.0.1 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - dev: false - - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - dev: true + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - /is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - dev: false + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true + isomorphic-ws@4.0.1: + resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} + peerDependencies: + ws: '*' - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + jayson@4.3.0: + resolution: {integrity: sha512-AauzHcUcqs8OBnCHOkJY280VaTiCm57AbuO7lqzcw7JapGj50BisE3xhksye4zlTSR1+1tAz67wLTl8tEH1obQ==} engines: {node: '>=8'} - dev: true + hasBin: true - /is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - dev: false - - /is-retry-allowed@2.2.0: - resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} - dev: false - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false - - /is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - dependencies: - better-path-resolve: 1.0.0 - dev: true + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - /is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - dependencies: - which-typed-array: 1.1.19 - dev: false + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} - /is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - dev: false + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - /is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - dev: false + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - /is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - dev: true + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - /isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: false + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: false + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - /isomorphic-ws@4.0.1(ws@7.5.10): - resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} - peerDependencies: - ws: '*' - dependencies: - ws: 7.5.10 + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - /isows@1.0.6(ws@8.18.0): - resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} - peerDependencies: - ws: '*' - dependencies: - ws: 8.18.0 - dev: false + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true - /isows@1.0.7(ws@8.18.3): - resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} peerDependencies: - ws: '*' - dependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - dev: false + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true - /jayson@4.2.0: - resolution: {integrity: sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==} - engines: {node: '>=8'} + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - dependencies: - '@types/connect': 3.4.38 - '@types/node': 12.20.55 - '@types/ws': 7.4.7 - commander: 2.20.3 - delay: 5.0.0 - es6-promisify: 5.0.0 - eyes: 0.1.8 - isomorphic-ws: 4.0.1(ws@7.5.10) - json-stringify-safe: 5.0.1 - stream-json: 1.9.1 - uuid: 8.3.2 - ws: 7.5.10 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - /jose@6.1.1: - resolution: {integrity: sha512-GWSqjfOPf4cWOkBzw5THBjtGPhXKqYnfRBzh4Ni+ArTrQQ9unvmsA3oFLqaYKoKe5sjWmGu5wVKg9Ft1i+LQfg==} - dev: false + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: false + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - dev: true + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - /js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - dependencies: - argparse: 2.0.1 - dev: true + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} - /json-rpc-engine@6.1.0: - resolution: {integrity: sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==} - engines: {node: '>=10.0.0'} - dependencies: - '@metamask/safe-event-emitter': 2.0.0 - eth-rpc-errors: 4.0.3 - dev: false + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - /json-rpc-random-id@1.0.1: - resolution: {integrity: sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA==} - dev: false + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - /json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true - /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - optionalDependencies: - graceful-fs: 4.2.11 - dev: true + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} - /keccak@3.0.4: - resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} - engines: {node: '>=10.0.0'} - requiresBuild: true - dependencies: - node-addon-api: 2.0.2 - node-gyp-build: 4.8.4 - readable-stream: 3.6.2 - dev: false + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - dependencies: - json-buffer: 3.0.1 - dev: true + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} - /keyvaluestorage-interface@1.0.0: - resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} - dev: false + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true + rpc-websockets@9.3.9: + resolution: {integrity: sha512-2iQDaTB4g5fDB2ihrTFSJSibCEuxaRi1q7qTW7ZO9/M5/TC+ToHA4D9/ffNLEbAoHNNrcdeP05oATNk44SKZXA==} - /lint-staged@16.2.6: - resolution: {integrity: sha512-s1gphtDbV4bmW1eylXpVMk2u7is7YsrLl8hzrtvC70h4ByhcMLZFY01Fx05ZUDNuv1H8HO4E+e2zgejV1jVwNw==} - engines: {node: '>=20.17'} - hasBin: true - dependencies: - commander: 14.0.2 - listr2: 9.0.5 - micromatch: 4.0.8 - nano-spawn: 2.0.0 - pidtree: 0.6.0 - string-argv: 0.3.2 - yaml: 2.8.1 - dev: true - - /listr2@9.0.5: - resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} - engines: {node: '>=20.0.0'} - dependencies: - cli-truncate: 5.1.1 - colorette: 2.0.20 - eventemitter3: 5.0.1 - log-update: 6.1.0 - rfdc: 1.4.1 - wrap-ansi: 9.0.2 - dev: true - - /lit-element@4.2.1: - resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} - dependencies: - '@lit-labs/ssr-dom-shim': 1.4.0 - '@lit/reactive-element': 2.1.1 - lit-html: 3.3.1 - dev: false + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - /lit-html@3.3.1: - resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} - dependencies: - '@types/trusted-types': 2.0.7 - dev: false + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - /lit@3.3.0: - resolution: {integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==} - dependencies: - '@lit/reactive-element': 2.1.1 - lit-element: 4.2.1 - lit-html: 3.3.1 - dev: false + socket.io-client@4.8.3: + resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} + engines: {node: '>=10.0.0'} - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 + socket.io-parser@4.2.6: + resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} + engines: {node: '>=10.0.0'} - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - dev: true + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} - /lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - dev: true + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: false + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - /log-symbols@6.0.0: - resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} - engines: {node: '>=18'} - dependencies: - chalk: 5.6.2 - is-unicode-supported: 1.3.0 - dev: false + stream-chain@2.2.5: + resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} - /log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} - engines: {node: '>=18'} - dependencies: - ansi-escapes: 7.2.0 - cli-cursor: 5.0.0 - slice-ansi: 7.1.2 - strip-ansi: 7.1.2 - wrap-ansi: 9.0.2 - dev: true - - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - dependencies: - js-tokens: 4.0.0 - dev: false + stream-json@1.9.1: + resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} - /lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - dev: false + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true - /magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - dev: true + superstruct@0.15.5: + resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} - /math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - dev: false + superstruct@2.0.2: + resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} + engines: {node: '>=14.0.0'} - /md5@2.3.0: - resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} - dependencies: - charenc: 0.0.2 - crypt: 0.0.2 - is-buffer: 1.1.6 - dev: false - - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true - - /micro-ftch@0.3.1: - resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==} - dev: false - - /micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - dev: true - - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: false - - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - dev: false + text-encoding-utf-8@1.0.2: + resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} - /mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.12 - dev: true + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - brace-expansion: 2.0.2 - dev: true + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - /mipd@0.0.7(typescript@5.9.3): - resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - typescript: 5.9.3 - dev: false + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: true + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} - /mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} - dev: true + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: false + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} - /multiformats@9.9.0: - resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} - dev: false + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} - /nano-spawn@2.0.0: - resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==} - engines: {node: '>=20.17'} - dev: true + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - /nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - dev: true - - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true - /node-addon-api@2.0.2: - resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} - dev: false + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - /node-fetch-native@1.6.7: - resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} - dev: false + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} + tsup@8.5.1: + resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} + engines: {node: '>=18'} + hasBin: true peerDependencies: - encoding: ^0.1.0 + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' peerDependenciesMeta: - encoding: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: optional: true - dependencies: - whatwg-url: 5.0.0 - - /node-gyp-build@4.8.4: - resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} - hasBin: true - requiresBuild: true - - /node-mock-http@1.0.3: - resolution: {integrity: sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==} - dev: false - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: false + tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} - /obj-multiplex@1.0.0: - resolution: {integrity: sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA==} - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - readable-stream: 2.3.8 - dev: false + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true - /ofetch@1.5.1: - resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} - dependencies: - destr: 2.0.5 - node-fetch-native: 1.6.7 - ufo: 1.6.1 - dev: false + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} - /on-exit-leak-free@0.2.0: - resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} - dev: false + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 + utf-8-validate@6.0.6: + resolution: {integrity: sha512-q3l3P9UtEEiAHcsgsqTgf9PPjctrDWoIXW3NpOHFdRDbLvu4DLIcxHangJ4RLrWkBcKjmcs/6NkerI8T/rE4LA==} + engines: {node: '>=6.14.2'} - /onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} - dependencies: - mimic-function: 5.0.1 + uuid@14.0.0: + resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} + hasBin: true - /openapi-fetch@0.13.8: - resolution: {integrity: sha512-yJ4QKRyNxE44baQ9mY5+r/kAzZ8yXMemtNAOFwOzRXJscdjSxxzWSNlyBAr+o5JjkUw9Lc3W7OIoca0cY3PYnQ==} - dependencies: - openapi-typescript-helpers: 0.0.15 - dev: false + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true - /openapi-typescript-helpers@0.0.15: - resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==} - dev: false + vite-node@2.1.9: + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - dev: true - - /ora@8.2.0: - resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} - engines: {node: '>=18'} - dependencies: - chalk: 5.6.2 - cli-cursor: 5.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 6.0.0 - stdin-discarder: 0.2.2 - string-width: 7.2.0 - strip-ansi: 7.1.2 - dev: false - - /outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - dev: true - - /ox@0.6.7(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true peerDependencies: - typescript: '>=5.4.0' + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 peerDependenciesMeta: - typescript: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: optional: true - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.1(typescript@5.9.3)(zod@3.25.76) - eventemitter3: 5.0.1 - typescript: 5.9.3 - transitivePeerDependencies: - - zod - dev: false - /ox@0.6.9(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} + vitest@2.1.9: + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true peerDependencies: - typescript: '>=5.4.0' + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.9 + '@vitest/ui': 2.1.9 + happy-dom: '*' + jsdom: '*' peerDependenciesMeta: - typescript: + '@edge-runtime/vm': optional: true - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.1(typescript@5.9.3)(zod@3.25.76) - eventemitter3: 5.0.1 - typescript: 5.9.3 - transitivePeerDependencies: - - zod - dev: false + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - /ox@0.9.14(typescript@5.9.3)(zod@4.1.12): - resolution: {integrity: sha512-lxZYCzGH00WtIPPrqXCrbSW/ZiKjigfII6R0Vu1eH2GpobmcwVheiivbCvsBZzmVZcNpwkabSamPP+ZNtdnKIQ==} + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + ws@7.5.11: + resolution: {integrity: sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==} + engines: {node: '>=8.3.0'} peerDependencies: - typescript: '>=5.4.0' + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 peerDependenciesMeta: - typescript: + bufferutil: + optional: true + utf-8-validate: optional: true - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.1(typescript@5.9.3)(zod@4.1.12) - eventemitter3: 5.0.1 - typescript: 5.9.3 - transitivePeerDependencies: - - zod - dev: false - /ox@0.9.6(typescript@5.9.3)(zod@3.22.4): - resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==} + ws@8.20.1: + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + engines: {node: '>=10.0.0'} peerDependencies: - typescript: '>=5.4.0' + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' peerDependenciesMeta: - typescript: + bufferutil: + optional: true + utf-8-validate: optional: true - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.1(typescript@5.9.3)(zod@3.22.4) - eventemitter3: 5.0.1 - typescript: 5.9.3 - transitivePeerDependencies: - - zod - dev: false - /ox@0.9.6(typescript@5.9.3)(zod@4.1.12): - resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} peerDependencies: - typescript: '>=5.4.0' + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' peerDependenciesMeta: - typescript: + bufferutil: + optional: true + utf-8-validate: optional: true + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + +snapshots: + + '@babel/runtime@7.29.2': {} + + '@coral-xyz/anchor-errors@0.31.1': {} + + '@coral-xyz/anchor@0.32.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 + '@coral-xyz/anchor-errors': 0.31.1 + '@coral-xyz/borsh': 0.31.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)) '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.1(typescript@5.9.3)(zod@4.1.12) - eventemitter3: 5.0.1 - typescript: 5.9.3 + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) + bn.js: 5.2.3 + bs58: 4.0.1 + buffer-layout: 1.2.2 + camelcase: 6.3.0 + cross-fetch: 3.2.0 + eventemitter3: 4.0.7 + pako: 2.1.0 + superstruct: 0.15.5 + toml: 3.0.0 transitivePeerDependencies: - - zod - dev: false - - /p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - dependencies: - p-map: 2.1.0 - dev: true + - bufferutil + - encoding + - typescript + - utf-8-validate - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + '@coral-xyz/borsh@0.31.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6))': dependencies: - p-try: 2.2.0 + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) + bn.js: 5.2.3 + buffer-layout: 1.2.2 - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - dev: true + '@esbuild/aix-ppc64@0.21.5': + optional: true - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 + '@esbuild/aix-ppc64@0.27.7': + optional: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - dependencies: - p-limit: 3.1.0 - dev: true + '@esbuild/android-arm64@0.21.5': + optional: true - /p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - dev: true + '@esbuild/android-arm64@0.27.7': + optional: true - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} + '@esbuild/android-arm@0.21.5': + optional: true - /package-manager-detector@0.2.11: - resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} - dependencies: - quansync: 0.2.11 - dev: true + '@esbuild/android-arm@0.27.7': + optional: true - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - dev: true + '@esbuild/android-x64@0.21.5': + optional: true - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} + '@esbuild/android-x64@0.27.7': + optional: true - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true + '@esbuild/darwin-arm64@0.21.5': + optional: true - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: true + '@esbuild/darwin-arm64@0.27.7': + optional: true - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: true + '@esbuild/darwin-x64@0.21.5': + optional: true - /pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - dev: true + '@esbuild/darwin-x64@0.27.7': + optional: true - /picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - dev: true + '@esbuild/freebsd-arm64@0.21.5': + optional: true - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} + '@esbuild/freebsd-arm64@0.27.7': + optional: true - /picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - dev: true + '@esbuild/freebsd-x64@0.21.5': + optional: true - /pidtree@0.6.0: - resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} - engines: {node: '>=0.10'} - hasBin: true - dev: true + '@esbuild/freebsd-x64@0.27.7': + optional: true - /pify@3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} - dev: false + '@esbuild/linux-arm64@0.21.5': + optional: true - /pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - dev: true + '@esbuild/linux-arm64@0.27.7': + optional: true - /pify@5.0.0: - resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} - engines: {node: '>=10'} - dev: false + '@esbuild/linux-arm@0.21.5': + optional: true - /pino-abstract-transport@0.5.0: - resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==} - dependencies: - duplexify: 4.1.3 - split2: 4.2.0 - dev: false + '@esbuild/linux-arm@0.27.7': + optional: true - /pino-std-serializers@4.0.0: - resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==} - dev: false + '@esbuild/linux-ia32@0.21.5': + optional: true - /pino@7.11.0: - resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} - hasBin: true - dependencies: - atomic-sleep: 1.0.0 - fast-redact: 3.5.0 - on-exit-leak-free: 0.2.0 - pino-abstract-transport: 0.5.0 - pino-std-serializers: 4.0.0 - process-warning: 1.0.0 - quick-format-unescaped: 4.0.4 - real-require: 0.1.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 2.8.0 - thread-stream: 0.15.2 - dev: false - - /pngjs@5.0.0: - resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} - engines: {node: '>=10.13.0'} - dev: false - - /pony-cause@2.1.11: - resolution: {integrity: sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==} - engines: {node: '>=12.0.0'} - dev: false + '@esbuild/linux-ia32@0.27.7': + optional: true - /porto@0.2.35(@tanstack/react-query@5.90.7)(@wagmi/core@2.22.1)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.38.6)(wagmi@2.19.2): - resolution: {integrity: sha512-gu9FfjjvvYBgQXUHWTp6n3wkTxVtEcqFotM7i3GEZeoQbvLGbssAicCz6hFZ8+xggrJWwi/RLmbwNra50SMmUQ==} - hasBin: true - peerDependencies: - '@tanstack/react-query': '>=5.59.0' - '@wagmi/core': '>=2.16.3' - expo-auth-session: '>=7.0.8' - expo-crypto: '>=15.0.7' - expo-web-browser: '>=15.0.8' - react: '>=18' - react-native: '>=0.81.4' - typescript: '>=5.4.0' - viem: '>=2.37.0' - wagmi: '>=2.0.0' - peerDependenciesMeta: - '@tanstack/react-query': - optional: true - expo-auth-session: - optional: true - expo-crypto: - optional: true - expo-web-browser: - optional: true - react: - optional: true - react-native: - optional: true - typescript: - optional: true - wagmi: - optional: true - dependencies: - '@tanstack/react-query': 5.90.7(react@18.3.1) - '@wagmi/core': 2.22.1(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.38.6) - hono: 4.10.4 - idb-keyval: 6.2.2 - mipd: 0.0.7(typescript@5.9.3) - ox: 0.9.14(typescript@5.9.3)(zod@4.1.12) - react: 18.3.1 - typescript: 5.9.3 - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - wagmi: 2.19.2(@tanstack/react-query@5.90.7)(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(viem@2.38.6)(ws@8.18.3)(zod@3.25.76) - zod: 4.1.12 - zustand: 5.0.8(react@18.3.1)(use-sync-external-store@1.4.0) - transitivePeerDependencies: - - '@types/react' - - immer - - use-sync-external-store - dev: false - - /possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - dev: false - - /postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - dev: true + '@esbuild/linux-loong64@0.21.5': + optional: true - /preact@10.24.2: - resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} - dev: false + '@esbuild/linux-loong64@0.27.7': + optional: true - /preact@10.27.2: - resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==} - dev: false + '@esbuild/linux-mips64el@0.21.5': + optional: true - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true + '@esbuild/linux-mips64el@0.27.7': + optional: true - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true + '@esbuild/linux-ppc64@0.21.5': + optional: true - /prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} - engines: {node: '>=14'} - hasBin: true - dev: true + '@esbuild/linux-ppc64@0.27.7': + optional: true - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: false + '@esbuild/linux-riscv64@0.21.5': + optional: true - /process-warning@1.0.0: - resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} - dev: false + '@esbuild/linux-riscv64@0.27.7': + optional: true - /proxy-compare@2.6.0: - resolution: {integrity: sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw==} - dev: false + '@esbuild/linux-s390x@0.21.5': + optional: true - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: false + '@esbuild/linux-s390x@0.27.7': + optional: true - /pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - dev: false + '@esbuild/linux-x64@0.21.5': + optional: true - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - dev: true + '@esbuild/linux-x64@0.27.7': + optional: true - /qrcode@1.5.3: - resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} - engines: {node: '>=10.13.0'} - hasBin: true - dependencies: - dijkstrajs: 1.0.3 - encode-utf8: 1.0.3 - pngjs: 5.0.0 - yargs: 15.4.1 - dev: false - - /quansync@0.2.11: - resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - dev: true - - /query-string@7.1.3: - resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} - engines: {node: '>=6'} - dependencies: - decode-uri-component: 0.2.2 - filter-obj: 1.1.0 - split-on-first: 1.1.0 - strict-uri-encode: 2.0.0 - dev: false - - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true - - /quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - dev: false - - /radix3@1.1.2: - resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} - dev: false - - /react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} - engines: {node: '>=0.10.0'} - dependencies: - loose-envify: 1.4.0 - dev: false + '@esbuild/netbsd-arm64@0.27.7': + optional: true - /read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.1 - pify: 4.0.1 - strip-bom: 3.0.0 - dev: true - - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - dev: false - - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - dev: false + '@esbuild/netbsd-x64@0.21.5': + optional: true - /readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - dev: false + '@esbuild/netbsd-x64@0.27.7': + optional: true - /real-require@0.1.0: - resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==} - engines: {node: '>= 12.13.0'} - dev: false + '@esbuild/openbsd-arm64@0.27.7': + optional: true - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: false + '@esbuild/openbsd-x64@0.21.5': + optional: true - /require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - dev: false + '@esbuild/openbsd-x64@0.27.7': + optional: true - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - dev: true + '@esbuild/openharmony-arm64@0.27.7': + optional: true - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - dev: true + '@esbuild/sunos-x64@0.21.5': + optional: true - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: true + '@esbuild/sunos-x64@0.27.7': + optional: true - /restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} - dependencies: - onetime: 7.0.0 - signal-exit: 4.1.0 + '@esbuild/win32-arm64@0.21.5': + optional: true - /reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true + '@esbuild/win32-arm64@0.27.7': + optional: true - /rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - dev: true + '@esbuild/win32-ia32@0.21.5': + optional: true - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - dependencies: - glob: 7.2.3 - dev: true + '@esbuild/win32-ia32@0.27.7': + optional: true - /rollup@4.53.2: - resolution: {integrity: sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.2 - '@rollup/rollup-android-arm64': 4.53.2 - '@rollup/rollup-darwin-arm64': 4.53.2 - '@rollup/rollup-darwin-x64': 4.53.2 - '@rollup/rollup-freebsd-arm64': 4.53.2 - '@rollup/rollup-freebsd-x64': 4.53.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.2 - '@rollup/rollup-linux-arm-musleabihf': 4.53.2 - '@rollup/rollup-linux-arm64-gnu': 4.53.2 - '@rollup/rollup-linux-arm64-musl': 4.53.2 - '@rollup/rollup-linux-loong64-gnu': 4.53.2 - '@rollup/rollup-linux-ppc64-gnu': 4.53.2 - '@rollup/rollup-linux-riscv64-gnu': 4.53.2 - '@rollup/rollup-linux-riscv64-musl': 4.53.2 - '@rollup/rollup-linux-s390x-gnu': 4.53.2 - '@rollup/rollup-linux-x64-gnu': 4.53.2 - '@rollup/rollup-linux-x64-musl': 4.53.2 - '@rollup/rollup-openharmony-arm64': 4.53.2 - '@rollup/rollup-win32-arm64-msvc': 4.53.2 - '@rollup/rollup-win32-ia32-msvc': 4.53.2 - '@rollup/rollup-win32-x64-gnu': 4.53.2 - '@rollup/rollup-win32-x64-msvc': 4.53.2 - fsevents: 2.3.3 - dev: true + '@esbuild/win32-x64@0.21.5': + optional: true - /rpc-websockets@9.3.1: - resolution: {integrity: sha512-bY6a+i/lEtBJ/mUxwsCTgevoV1P0foXTVA7UoThzaIWbM+3NDqorf8NBWs5DmqKTFeA1IoNzgvkWjFCPgnzUiQ==} - dependencies: - '@swc/helpers': 0.5.17 - '@types/uuid': 8.3.4 - '@types/ws': 8.18.1 - buffer: 6.0.3 - eventemitter3: 5.0.1 - uuid: 8.3.2 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 + '@esbuild/win32-x64@0.27.7': + optional: true - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + '@jridgewell/gen-mapping@0.3.13': dependencies: - queue-microtask: 1.2.3 - dev: true + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: false + '@jridgewell/resolve-uri@3.1.2': {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + '@jridgewell/sourcemap-codec@1.5.5': {} - /safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} + '@jridgewell/trace-mapping@0.3.31': dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - dev: false + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 - /safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - dev: false + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true + '@noble/hashes@1.8.0': {} - /semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true + '@openfacilitator/sdk@1.0.0': {} - /set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - dev: false + '@rollup/rollup-android-arm-eabi@4.60.4': + optional: true - /set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - dev: false - - /sha.js@2.4.12: - resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} - engines: {node: '>= 0.10'} - hasBin: true - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - dev: false + '@rollup/rollup-android-arm64@4.60.4': + optional: true - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - dependencies: - shebang-regex: 3.0.0 - dev: true + '@rollup/rollup-darwin-arm64@4.60.4': + optional: true - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: true + '@rollup/rollup-darwin-x64@4.60.4': + optional: true - /siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - dev: true + '@rollup/rollup-freebsd-arm64@4.60.4': + optional: true - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} + '@rollup/rollup-freebsd-x64@4.60.4': + optional: true - /sirv@3.0.2: - resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} - engines: {node: '>=18'} - dependencies: - '@polka/url': 1.0.0-next.29 - mrmime: 2.0.1 - totalist: 3.0.1 - dev: true + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + optional: true - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + optional: true - /slice-ansi@7.1.2: - resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} - engines: {node: '>=18'} - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 5.1.0 - dev: true + '@rollup/rollup-linux-arm64-gnu@4.60.4': + optional: true - /socket.io-client@4.8.1: - resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} - engines: {node: '>=10.0.0'} - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.3.4 - engine.io-client: 6.6.3 - socket.io-parser: 4.2.4 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: false + '@rollup/rollup-linux-arm64-musl@4.60.4': + optional: true - /socket.io-parser@4.2.4: - resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} - engines: {node: '>=10.0.0'} - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false + '@rollup/rollup-linux-loong64-gnu@4.60.4': + optional: true - /sonic-boom@2.8.0: - resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} - dependencies: - atomic-sleep: 1.0.0 - dev: false + '@rollup/rollup-linux-loong64-musl@4.60.4': + optional: true - /source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - dev: true + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + optional: true - /spawndamnit@3.0.1: - resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - dev: true + '@rollup/rollup-linux-ppc64-musl@4.60.4': + optional: true - /split-on-first@1.1.0: - resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} - engines: {node: '>=6'} - dev: false + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + optional: true - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - dev: false + '@rollup/rollup-linux-riscv64-musl@4.60.4': + optional: true - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true + '@rollup/rollup-linux-s390x-gnu@4.60.4': + optional: true - /stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - dev: true + '@rollup/rollup-linux-x64-gnu@4.60.4': + optional: true - /std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - dev: true + '@rollup/rollup-linux-x64-musl@4.60.4': + optional: true - /stdin-discarder@0.2.2: - resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} - engines: {node: '>=18'} - dev: false + '@rollup/rollup-openbsd-x64@4.60.4': + optional: true - /stream-chain@2.2.5: - resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} + '@rollup/rollup-openharmony-arm64@4.60.4': + optional: true - /stream-json@1.9.1: - resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} - dependencies: - stream-chain: 2.2.5 + '@rollup/rollup-win32-arm64-msvc@4.60.4': + optional: true - /stream-shift@1.0.3: - resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} - dev: false + '@rollup/rollup-win32-ia32-msvc@4.60.4': + optional: true - /strict-uri-encode@2.0.0: - resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} - engines: {node: '>=4'} - dev: false + '@rollup/rollup-win32-x64-gnu@4.60.4': + optional: true - /string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - dev: true + '@rollup/rollup-win32-x64-msvc@4.60.4': + optional: true - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - dev: false + '@socket.io/component-emitter@3.1.2': {} - /string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} + '@solana/buffer-layout@4.0.1': dependencies: - emoji-regex: 10.6.0 - get-east-asian-width: 1.4.0 - strip-ansi: 7.1.2 + buffer: 6.0.3 - /string-width@8.1.0: - resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} - engines: {node: '>=20'} + '@solana/codecs-core@2.3.0(typescript@5.9.3)': dependencies: - get-east-asian-width: 1.4.0 - strip-ansi: 7.1.2 - dev: true + '@solana/errors': 2.3.0(typescript@5.9.3) + typescript: 5.9.3 - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + '@solana/codecs-numbers@2.3.0(typescript@5.9.3)': dependencies: - safe-buffer: 5.1.2 - dev: false + '@solana/codecs-core': 2.3.0(typescript@5.9.3) + '@solana/errors': 2.3.0(typescript@5.9.3) + typescript: 5.9.3 - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + '@solana/errors@2.3.0(typescript@5.9.3)': dependencies: - safe-buffer: 5.2.1 - dev: false + chalk: 5.6.2 + commander: 14.0.3 + typescript: 5.9.3 - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + '@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: - ansi-regex: 5.0.1 + '@babel/runtime': 7.29.2 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@solana/buffer-layout': 4.0.1 + '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) + agentkeepalive: 4.6.0 + bn.js: 5.2.3 + borsh: 0.7.0 + bs58: 4.0.1 + buffer: 6.0.3 + fast-stable-stringify: 1.0.0 + jayson: 4.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + node-fetch: 2.7.0 + rpc-websockets: 9.3.9 + superstruct: 2.0.2 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate - /strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} + '@swc/helpers@0.5.21': dependencies: - ansi-regex: 6.2.2 - - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true - - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true - - /superstruct@1.0.4: - resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==} - engines: {node: '>=14.0.0'} - dev: false - - /superstruct@2.0.2: - resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} - engines: {node: '>=14.0.0'} + tslib: 2.8.1 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + '@types/connect@3.4.38': dependencies: - has-flag: 4.0.0 - dev: true + '@types/node': 22.19.19 - /term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - dev: true + '@types/estree@1.0.8': {} - /text-encoding-utf-8@1.0.2: - resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} + '@types/estree@1.0.9': {} - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true + '@types/node@12.20.55': {} - /thread-stream@0.15.2: - resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} + '@types/node@22.19.19': dependencies: - real-require: 0.1.0 - dev: false - - /tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - dev: true + undici-types: 6.21.0 - /tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - dev: true + '@types/uuid@10.0.0': {} - /tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} + '@types/ws@7.4.7': dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - dev: true - - /tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} - engines: {node: '>=14.0.0'} - dev: true + '@types/node': 22.19.19 - /to-buffer@1.2.2: - resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} - engines: {node: '>= 0.4'} + '@types/ws@8.18.1': dependencies: - isarray: 2.0.5 - safe-buffer: 5.2.1 - typed-array-buffer: 1.0.3 - dev: false + '@types/node': 22.19.19 - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + '@vitest/expect@2.1.9': dependencies: - is-number: 7.0.0 - dev: true - - /totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - dev: true + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + tinyrainbow: 1.2.0 - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - - /ts-api-utils@1.4.3(typescript@5.9.3): - resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' + '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.19.19))': dependencies: - typescript: 5.9.3 - dev: true + '@vitest/spy': 2.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.21(@types/node@22.19.19) - /tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: false + '@vitest/pretty-format@2.1.9': + dependencies: + tinyrainbow: 1.2.0 - /tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + '@vitest/runner@2.1.9': + dependencies: + '@vitest/utils': 2.1.9 + pathe: 1.1.2 - /tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + '@vitest/snapshot@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + magic-string: 0.30.21 + pathe: 1.1.2 - /tsx@4.20.6: - resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} - engines: {node: '>=18.0.0'} - hasBin: true + '@vitest/spy@2.1.9': dependencies: - esbuild: 0.25.12 - get-tsconfig: 4.13.0 - optionalDependencies: - fsevents: 2.3.3 - dev: true + tinyspy: 3.0.2 - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + '@vitest/utils@2.1.9': dependencies: - prelude-ls: 1.2.1 - dev: true + '@vitest/pretty-format': 2.1.9 + loupe: 3.2.1 + tinyrainbow: 1.2.0 - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - dev: true + acorn@8.16.0: {} - /typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} + agentkeepalive@4.6.0: dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - dev: false + humanize-ms: 1.2.1 - /typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true + any-promise@1.3.0: {} - /ufo@1.6.1: - resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - dev: false + assertion-error@2.0.1: {} - /uint8arrays@3.1.0: - resolution: {integrity: sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==} + base-x@3.0.11: dependencies: - multiformats: 9.9.0 - dev: false - - /uncrypto@0.1.3: - resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} - dev: false - - /undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + safe-buffer: 5.2.1 - /undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + base-x@5.0.1: {} - /undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - dev: false + base64-js@1.5.1: {} - /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true + bn.js@5.2.3: {} - /unstorage@1.17.2(idb-keyval@6.2.2): - resolution: {integrity: sha512-cKEsD6iBWJgOMJ6vW1ID/SYuqNf8oN4yqRk8OYqaVQ3nnkJXOT1PSpaMh2QfzLs78UN5kSNRD2c/mgjT8tX7+w==} - peerDependencies: - '@azure/app-configuration': ^1.8.0 - '@azure/cosmos': ^4.2.0 - '@azure/data-tables': ^13.3.0 - '@azure/identity': ^4.6.0 - '@azure/keyvault-secrets': ^4.9.0 - '@azure/storage-blob': ^12.26.0 - '@capacitor/preferences': ^6.0.3 || ^7.0.0 - '@deno/kv': '>=0.9.0' - '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 - '@planetscale/database': ^1.19.0 - '@upstash/redis': ^1.34.3 - '@vercel/blob': '>=0.27.1' - '@vercel/functions': ^2.2.12 || ^3.0.0 - '@vercel/kv': ^1.0.1 - aws4fetch: ^1.0.20 - db0: '>=0.2.1' - idb-keyval: ^6.2.1 - ioredis: ^5.4.2 - uploadthing: ^7.4.4 - peerDependenciesMeta: - '@azure/app-configuration': - optional: true - '@azure/cosmos': - optional: true - '@azure/data-tables': - optional: true - '@azure/identity': - optional: true - '@azure/keyvault-secrets': - optional: true - '@azure/storage-blob': - optional: true - '@capacitor/preferences': - optional: true - '@deno/kv': - optional: true - '@netlify/blobs': - optional: true - '@planetscale/database': - optional: true - '@upstash/redis': - optional: true - '@vercel/blob': - optional: true - '@vercel/functions': - optional: true - '@vercel/kv': - optional: true - aws4fetch: - optional: true - db0: - optional: true - idb-keyval: - optional: true - ioredis: - optional: true - uploadthing: - optional: true + borsh@0.7.0: dependencies: - anymatch: 3.1.3 - chokidar: 4.0.3 - destr: 2.0.5 - h3: 1.15.4 - idb-keyval: 6.2.2 - lru-cache: 10.4.3 - node-fetch-native: 1.6.7 - ofetch: 1.5.1 - ufo: 1.6.1 - dev: false - - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + bn.js: 5.2.3 + bs58: 4.0.1 + text-encoding-utf-8: 1.0.2 + + bs58@4.0.1: dependencies: - punycode: 2.3.1 - dev: true + base-x: 3.0.11 - /use-sync-external-store@1.2.0(react@18.3.1): - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + bs58@6.0.0: dependencies: - react: 18.3.1 - dev: false + base-x: 5.0.1 - /use-sync-external-store@1.4.0(react@18.3.1): - resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + buffer-layout@1.2.2: {} + + buffer@6.0.3: dependencies: - react: 18.3.1 - dev: false + base64-js: 1.5.1 + ieee754: 1.2.1 - /utf-8-validate@5.0.10: - resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} - engines: {node: '>=6.14.2'} - requiresBuild: true + bufferutil@4.1.0: dependencies: node-gyp-build: 4.8.4 + optional: true - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: false - - /util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + bundle-require@5.1.0(esbuild@0.27.7): dependencies: - inherits: 2.0.4 - is-arguments: 1.2.0 - is-generator-function: 1.1.2 - is-typed-array: 1.1.15 - which-typed-array: 1.1.19 - dev: false - - /uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true + esbuild: 0.27.7 + load-tsconfig: 0.2.5 - /uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - dev: false + cac@6.7.14: {} - /valtio@1.13.2(react@18.3.1): - resolution: {integrity: sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=16.8' - react: '>=16.8' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - dependencies: - derive-valtio: 0.1.0(valtio@1.13.2) - proxy-compare: 2.6.0 - react: 18.3.1 - use-sync-external-store: 1.2.0(react@18.3.1) - dev: false - - /viem@2.23.2(typescript@5.9.3)(zod@3.25.76): - resolution: {integrity: sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 - '@scure/bip32': 1.6.2 - '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) - isows: 1.0.6(ws@8.18.0) - ox: 0.6.7(typescript@5.9.3)(zod@3.25.76) - typescript: 5.9.3 - ws: 8.18.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - dev: false + camelcase@6.3.0: {} - /viem@2.38.6(typescript@5.9.3)(zod@3.22.4): - resolution: {integrity: sha512-aqO6P52LPXRjdnP6rl5Buab65sYa4cZ6Cpn+k4OLOzVJhGIK8onTVoKMFMT04YjDfyDICa/DZyV9HmvLDgcjkw==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true + chai@5.3.3: dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.3)(zod@3.22.4) - isows: 1.0.7(ws@8.18.3) - ox: 0.9.6(typescript@5.9.3)(zod@3.22.4) - typescript: 5.9.3 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - dev: false + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 - /viem@2.38.6(typescript@5.9.3)(zod@4.1.12): - resolution: {integrity: sha512-aqO6P52LPXRjdnP6rl5Buab65sYa4cZ6Cpn+k4OLOzVJhGIK8onTVoKMFMT04YjDfyDICa/DZyV9HmvLDgcjkw==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.3)(zod@4.1.12) - isows: 1.0.7(ws@8.18.3) - ox: 0.9.6(typescript@5.9.3)(zod@4.1.12) - typescript: 5.9.3 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - dev: false + chalk@5.6.2: {} - /vite@7.2.2(@types/node@20.19.24)(tsx@4.20.6): - resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true + check-error@2.1.3: {} + + chokidar@4.0.3: dependencies: - '@types/node': 20.19.24 - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.53.2 - tinyglobby: 0.2.15 - tsx: 4.20.6 - optionalDependencies: - fsevents: 2.3.3 - dev: true + readdirp: 4.1.2 - /vitest@4.0.8(@types/node@20.19.24)(@vitest/ui@4.0.8)(tsx@4.20.6): - resolution: {integrity: sha512-urzu3NCEV0Qa0Y2PwvBtRgmNtxhj5t5ULw7cuKhIHh3OrkKTLlut0lnBOv9qe5OvbkMH2g38G7KPDCTpIytBVg==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.8 - '@vitest/browser-preview': 4.0.8 - '@vitest/browser-webdriverio': 4.0.8 - '@vitest/ui': 4.0.8 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/debug': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true + commander@14.0.3: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + confbox@0.1.8: {} + + consola@3.4.2: {} + + cross-fetch@3.2.0: dependencies: - '@types/node': 20.19.24 - '@vitest/expect': 4.0.8 - '@vitest/mocker': 4.0.8(vite@7.2.2) - '@vitest/pretty-format': 4.0.8 - '@vitest/runner': 4.0.8 - '@vitest/snapshot': 4.0.8 - '@vitest/spy': 4.0.8 - '@vitest/ui': 4.0.8(vitest@4.0.8) - '@vitest/utils': 4.0.8 - debug: 4.4.3 - es-module-lexer: 1.7.0 - expect-type: 1.2.2 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.2.2(@types/node@20.19.24)(tsx@4.20.6) - why-is-node-running: 2.3.0 + node-fetch: 2.7.0 transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - dev: true + - encoding - /wagmi@2.19.2(@tanstack/react-query@5.90.7)(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(viem@2.38.6)(ws@8.18.3)(zod@3.25.76): - resolution: {integrity: sha512-UN8CQKNsUgQD2Lr2ybjAi07ZKq1SV4T/Pb9IN77t3oSXANr9C9tI3mijLNyINeA5ET4hJxIny3C2dWJ48zrFiA==} - peerDependencies: - '@tanstack/react-query': '>=5.0.0' - react: '>=18' - typescript: '>=5.0.4' - viem: 2.x - peerDependenciesMeta: - typescript: - optional: true + debug@4.4.3: dependencies: - '@tanstack/react-query': 5.90.7(react@18.3.1) - '@wagmi/connectors': 6.1.3(@tanstack/react-query@5.90.7)(@wagmi/core@2.22.1)(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.38.6)(wagmi@2.19.2)(ws@8.18.3)(zod@3.25.76) - '@wagmi/core': 2.22.1(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.38.6) - react: 18.3.1 - typescript: 5.9.3 - use-sync-external-store: 1.4.0(react@18.3.1) - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) + ms: 2.1.3 + + deep-eql@5.0.2: {} + + delay@5.0.0: {} + + engine.io-client@6.6.5(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-parser: 5.2.3 + ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) + xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@tanstack/query-core' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - bufferutil - - db0 - - debug - - encoding - - expo-auth-session - - expo-crypto - - expo-web-browser - - fastestsmallesttextencoderdecoder - - immer - - ioredis - - react-native - supports-color - - uploadthing - utf-8-validate - - ws - - zod - dev: false - /webextension-polyfill@0.10.0: - resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==} - dev: false + engine.io-parser@5.2.3: {} - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + es-module-lexer@1.7.0: {} - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + es6-promise@4.2.8: {} + + es6-promisify@5.0.0: dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 + es6-promise: 4.2.8 - /which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - dev: false + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.4: {} + + expect-type@1.3.0: {} + + eyes@0.1.8: {} + + fast-stable-stringify@1.0.0: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 - /which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} + fix-dts-default-cjs-exports@1.0.1: dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - dev: false - - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + magic-string: 0.30.21 + mlly: 1.8.2 + rollup: 4.60.4 + + fsevents@2.3.3: + optional: true + + humanize-ms@1.2.1: dependencies: - isexe: 2.0.0 - dev: true + ms: 2.1.3 - /why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true + ieee754@1.2.1: {} + + isomorphic-ws@4.0.1(ws@7.5.11(bufferutil@4.1.0)(utf-8-validate@6.0.6)): dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - dev: true + ws: 7.5.11(bufferutil@4.1.0)(utf-8-validate@6.0.6) - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - dev: true + jayson@4.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + '@types/connect': 3.4.38 + '@types/node': 12.20.55 + '@types/ws': 7.4.7 + commander: 2.20.3 + delay: 5.0.0 + es6-promisify: 5.0.0 + eyes: 0.1.8 + isomorphic-ws: 4.0.1(ws@7.5.11(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + json-stringify-safe: 5.0.1 + stream-json: 1.9.1 + uuid: 8.3.2 + ws: 7.5.11(bufferutil@4.1.0)(utf-8-validate@6.0.6) + transitivePeerDependencies: + - bufferutil + - utf-8-validate - /wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + joycon@3.1.1: {} + + json-stringify-safe@5.0.1: {} + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + load-tsconfig@0.2.5: {} + + loupe@3.2.1: {} + + magic-string@0.30.21: dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: false + '@jridgewell/sourcemap-codec': 1.5.5 - /wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} - engines: {node: '>=18'} + mlly@1.8.2: dependencies: - ansi-styles: 6.2.3 - string-width: 7.2.0 - strip-ansi: 7.1.2 - dev: true + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ms@2.1.3: {} - /ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 - /ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} - 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 + nanoid@3.3.12: {} - /ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - 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 - dev: false + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 - /ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} - 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-gyp-build@4.8.4: + optional: true + + object-assign@4.1.1: {} + + pako@2.1.0: {} + + pathe@1.1.2: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + pirates@4.0.7: {} + + pkg-types@1.3.1: dependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 - /x402-solana@0.1.4(@solana/spl-token@0.4.14)(@solana/sysvars@2.3.0)(@solana/web3.js@1.98.4)(@tanstack/react-query@5.90.7)(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(ws@8.18.3)(zod@4.1.12): - resolution: {integrity: sha512-plMGCDhgh5uVxCYzdNAjY1ba5cA8LlO2Iw6mAoiqRYHevwzGWPtlUmIbLzGwpawIXusQCinjKMVkCMmr0PmDJw==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@solana/spl-token': '>=0.4.0' - '@solana/web3.js': '>=1.90.0' - zod: '>=3.20.0' + postcss-load-config@6.0.1(postcss@8.5.15): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + postcss: 8.5.15 + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + readdirp@4.1.2: {} + + resolve-from@5.0.0: {} + + rollup@4.60.4: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 + fsevents: 2.3.3 + + rpc-websockets@9.3.9: + dependencies: + '@swc/helpers': 0.5.21 + '@types/uuid': 10.0.0 + '@types/ws': 8.18.1 + buffer: 6.0.3 + eventemitter3: 5.0.4 + uuid: 14.0.0 + ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 6.0.6 + + safe-buffer@5.2.1: {} + + siginfo@2.0.0: {} + + socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: - '@solana/spl-token': 0.4.14(@solana/web3.js@1.98.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/web3.js': 1.98.4(typescript@5.9.3) - x402: 0.6.6(@solana/sysvars@2.3.0)(@tanstack/react-query@5.90.7)(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(ws@8.18.3) - zod: 4.1.12 + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-client: 6.6.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) + socket.io-parser: 4.2.6 transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@solana/sysvars' - - '@tanstack/query-core' - - '@tanstack/react-query' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - bufferutil - - db0 - - debug - - encoding - - expo-auth-session - - expo-crypto - - expo-web-browser - - fastestsmallesttextencoderdecoder - - immer - - ioredis - - react - - react-native - supports-color - - typescript - - uploadthing - utf-8-validate - - ws - dev: false - /x402@0.6.6(@solana/sysvars@2.3.0)(@tanstack/react-query@5.90.7)(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(ws@8.18.3): - resolution: {integrity: sha512-gKkxqKBT0mH7fSLld6Mz9ML52dmu1XeOPhVtiUCA3EzkfY6p0EJ3ijKhIKN0Jh8Q6kVXrFlJ/4iAUS1dNDA2lA==} + socket.io-parser@4.2.6: dependencies: - '@scure/base': 1.2.6 - '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0) - '@solana-program/token': 0.5.1(@solana/kit@2.3.0) - '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0)(@solana/sysvars@2.3.0) - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3) - viem: 2.38.6(typescript@5.9.3)(zod@4.1.12) - wagmi: 2.19.2(@tanstack/react-query@5.90.7)(fastestsmallesttextencoderdecoder@1.0.22)(react@18.3.1)(typescript@5.9.3)(viem@2.38.6)(ws@8.18.3)(zod@3.25.76) - zod: 3.25.76 + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@solana/sysvars' - - '@tanstack/query-core' - - '@tanstack/react-query' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - debug - - encoding - - expo-auth-session - - expo-crypto - - expo-web-browser - - fastestsmallesttextencoderdecoder - - immer - - ioredis - - react - - react-native - supports-color - - typescript - - uploadthing - - utf-8-validate - - ws - dev: false - /xmlhttprequest-ssl@2.1.2: - resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} - engines: {node: '>=0.4.0'} - dev: false + source-map-js@1.2.1: {} - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - dev: false + source-map@0.7.6: {} - /y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - dev: false + stackback@0.0.2: {} - /yaml@2.8.1: - resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} - engines: {node: '>= 14.6'} - hasBin: true - dev: true + std-env@3.10.0: {} - /yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} + stream-chain@2.2.5: {} + + stream-json@1.9.1: dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - dev: false + stream-chain: 2.2.5 - /yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} + sucrase@3.35.1: dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 - dev: false - - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.16 + ts-interface-checker: 0.1.13 - /zod@3.22.4: - resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - dev: false + superstruct@0.15.5: {} - /zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - dev: false + superstruct@2.0.2: {} - /zod@4.1.12: - resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} - dev: false + text-encoding-utf-8@1.0.2: {} - /zustand@5.0.0(react@18.3.1)(use-sync-external-store@1.4.0): - resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true + thenify-all@1.6.0: dependencies: - react: 18.3.1 - use-sync-external-store: 1.4.0(react@18.3.1) - dev: false + thenify: 3.3.1 - /zustand@5.0.3(react@18.3.1)(use-sync-external-store@1.4.0): - resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true + thenify@3.3.1: dependencies: - react: 18.3.1 - use-sync-external-store: 1.4.0(react@18.3.1) - dev: false + any-promise: 1.3.0 - /zustand@5.0.8(react@18.3.1)(use-sync-external-store@1.4.0): - resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinypool@1.1.1: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + + toml@3.0.0: {} + + tr46@0.0.3: {} + + tree-kill@1.2.2: {} + + ts-interface-checker@0.1.13: {} + + tslib@2.8.1: {} + + tsup@8.5.1(postcss@8.5.15)(typescript@5.9.3): + dependencies: + bundle-require: 5.1.0(esbuild@0.27.7) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.3 + esbuild: 0.27.7 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.15) + resolve-from: 5.0.0 + rollup: 4.60.4 + source-map: 0.7.6 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.16 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.15 + typescript: 5.9.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + + tweetnacl@1.0.3: {} + + typescript@5.9.3: {} + + ufo@1.6.4: {} + + undici-types@6.21.0: {} + + utf-8-validate@6.0.6: + dependencies: + node-gyp-build: 4.8.4 + optional: true + + uuid@14.0.0: {} + + uuid@8.3.2: {} + + vite-node@2.1.9(@types/node@22.19.19): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 1.1.2 + vite: 5.4.21(@types/node@22.19.19) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.21(@types/node@22.19.19): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.15 + rollup: 4.60.4 + optionalDependencies: + '@types/node': 22.19.19 + fsevents: 2.3.3 + + vitest@2.1.9(@types/node@22.19.19): + dependencies: + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.19.19)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 1.1.2 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.1.1 + tinyrainbow: 1.2.0 + vite: 5.4.21(@types/node@22.19.19) + vite-node: 2.1.9(@types/node@22.19.19) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.19 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + why-is-node-running@2.3.0: dependencies: - react: 18.3.1 - use-sync-external-store: 1.4.0(react@18.3.1) - dev: false + siginfo: 2.0.0 + stackback: 0.0.2 + + ws@7.5.11(bufferutil@4.1.0)(utf-8-validate@6.0.6): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 6.0.6 + + ws@8.20.1(bufferutil@4.1.0)(utf-8-validate@6.0.6): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 6.0.6 + + ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 6.0.6 + + xmlhttprequest-ssl@2.1.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index 33396df..0000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,5 +0,0 @@ -packages: - - 'packages/*' - - 'apps/*' - - 'examples/*' - diff --git a/scripts/generate-base-wallet.ts b/scripts/generate-base-wallet.ts deleted file mode 100644 index 68cf522..0000000 --- a/scripts/generate-base-wallet.ts +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env tsx -/** - * Generate a Base/EVM wallet for local testing - * - * Usage: - * pnpm tsx scripts/generate-base-wallet.ts - * - * Outputs: - * - Private key (keep this secret!) - * - Address (public address for receiving funds) - * - Saves to ~/.memeputer/base-wallet.json - */ - -import { Wallet } from 'ethers'; -import { writeFileSync, mkdirSync, existsSync } from 'fs'; -import { join } from 'path'; -import { homedir } from 'os'; - -// Generate a random wallet -const wallet = Wallet.createRandom(); - -// Create directory if it doesn't exist -const configDir = join(homedir(), '.memeputer'); -if (!existsSync(configDir)) { - mkdirSync(configDir, { recursive: true }); -} - -// Save wallet to file -const walletPath = join(configDir, 'base-wallet.json'); -const walletData = { - address: wallet.address, - privateKey: wallet.privateKey, - mnemonic: wallet.mnemonic?.phrase, -}; - -writeFileSync(walletPath, JSON.stringify(walletData, null, 2)); - -console.log('βœ… Base Wallet Generated!\n'); -console.log('πŸ“ Saved to:', walletPath); -console.log('\nπŸ“‹ Wallet Details:'); -console.log(' Address:', wallet.address); -console.log(' Private Key:', wallet.privateKey); -console.log(' Mnemonic:', wallet.mnemonic?.phrase); -console.log('\n⚠️ IMPORTANT: Keep your private key secret!'); -console.log('\nπŸ’‘ To use this wallet, add to your .env file:'); -console.log(''); -console.log(' MEMEPUTER_CHAIN=base'); -console.log(' MEMEPUTER_WALLET_PRIVATE_KEY=' + wallet.privateKey); -console.log(''); -console.log(' Or export in your shell:'); -console.log(' export MEMEPUTER_CHAIN=base'); -console.log(' export MEMEPUTER_WALLET_PRIVATE_KEY=' + wallet.privateKey); -console.log('\nπŸ’° Fund this wallet on Base Sepolia testnet:'); -console.log(' https://www.alchemy.com/faucets/base-sepolia'); - diff --git a/scripts/generate-solana-wallet.ts b/scripts/generate-solana-wallet.ts deleted file mode 100644 index 0c8ce4e..0000000 --- a/scripts/generate-solana-wallet.ts +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env tsx -/** - * Generate a Solana wallet for local testing - * - * Usage: - * pnpm tsx scripts/generate-solana-wallet.ts - * - * Outputs: - * - Public key (address) - * - Secret key (keep this secret!) - * - Saves to ~/.config/solana/id.json (standard Solana CLI location) - */ - -import { Keypair } from '@solana/web3.js'; -import { writeFileSync, mkdirSync, existsSync } from 'fs'; -import { join } from 'path'; -import { homedir } from 'os'; - -// Generate a random keypair -const keypair = Keypair.generate(); - -// Create Solana config directory if it doesn't exist -const solanaDir = join(homedir(), '.config', 'solana'); -if (!existsSync(solanaDir)) { - mkdirSync(solanaDir, { recursive: true }); -} - -// Save wallet to standard Solana CLI location -const walletPath = join(solanaDir, 'id.json'); -const secretKeyArray = Array.from(keypair.secretKey); - -// Check if wallet already exists -if (existsSync(walletPath)) { - console.log('⚠️ Warning: Wallet already exists at', walletPath); - console.log(' Saving backup to id-backup.json\n'); - - // Backup existing wallet - const backupPath = join(solanaDir, 'id-backup.json'); - const existingWallet = require(walletPath); - writeFileSync(backupPath, JSON.stringify(existingWallet)); -} - -writeFileSync(walletPath, JSON.stringify(secretKeyArray)); - -console.log('βœ… Solana Wallet Generated!\n'); -console.log('πŸ“ Saved to:', walletPath); -console.log('\nπŸ“‹ Wallet Details:'); -console.log(' Public Key (Address):', keypair.publicKey.toString()); -console.log('\n⚠️ IMPORTANT: Keep your secret key safe!'); -console.log(' Secret key saved to:', walletPath); -console.log('\nπŸ’‘ To use this wallet, add to your .env file:'); -console.log(''); -console.log(' MEMEPUTER_CHAIN=solana'); -console.log(' MEMEPUTER_WALLET=' + walletPath); -console.log(''); -console.log(' Or export in your shell:'); -console.log(' export MEMEPUTER_CHAIN=solana'); -console.log(' export MEMEPUTER_WALLET=' + walletPath); -console.log('\nπŸ’° Fund this wallet on Solana devnet:'); -console.log(' solana airdrop 1 ' + keypair.publicKey.toString() + ' --url devnet'); -console.log('\nπŸ’° Or get mainnet SOL:'); -console.log(' https://phantom.app (buy/transfer SOL)'); -console.log(' Then swap for USDC on Jupiter: https://jup.ag'); - diff --git a/scripts/post-build.mjs b/scripts/post-build.mjs new file mode 100644 index 0000000..2186b36 --- /dev/null +++ b/scripts/post-build.mjs @@ -0,0 +1,15 @@ +import { readFileSync, writeFileSync, chmodSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; + +const CLI = join('dist', 'cli.mjs'); +if (!existsSync(CLI)) { + console.error(`[post-build] ${CLI} not found β€” did tsup build the cli entry?`); + process.exit(1); +} +const SHEBANG = '#!/usr/bin/env node\n'; +const current = readFileSync(CLI, 'utf8'); +if (!current.startsWith(SHEBANG)) { + writeFileSync(CLI, SHEBANG + current, 'utf8'); +} +chmodSync(CLI, 0o755); +console.log('[post-build] shebang + chmod applied to', CLI); diff --git a/src/agents.ts b/src/agents.ts new file mode 100644 index 0000000..606a4f5 --- /dev/null +++ b/src/agents.ts @@ -0,0 +1,84 @@ +import type { MemeputerClient } from './client.js'; +import type { ApiAgentProfile, ApiAgentSummary } from './types.js'; +import { + registerWithX402, + type RegisterAgentBody, + type RegisterAgentResponse, +} from './x402.js'; + +/** + * PATCH /v1/agents/:wallet body. Mirrors `patchAgentBodySchema` in + * apps/api/src/routes/agents.schemas.ts β€” `avatarUrl` / `bio` accept + * explicit null to clear; at least one field required (server enforces). + */ +export interface PatchAgentBody { + username?: string; + displayName?: string; + avatarUrl?: string | null; + bio?: string | null; +} + +/** + * AgentsNamespace β€” `mp.agents.*` wraps the /v1/agents/* endpoints. + * + * Composition (not subclass) over MemeputerClient: holds a reference and + * calls `this.client.signedRequest(...)` / `this.client.get(...)`. BLOCKER #5: + * signedRequest is `public` on the client so composition resolves at compile + * time. + */ +export class AgentsNamespace { + constructor(private readonly client: MemeputerClient) {} + + /** + * POST /v1/agents β€” x402 USDC payment required. xPayment header constructed + * by the caller via @openfacilitator/sdk's createPayment helper. BLOCKER #3: + * register sends ONLY `X-PAYMENT` β€” NO canonical-sig X-Memeputer-* headers. + */ + register(body: RegisterAgentBody, xPayment: string): Promise { + return registerWithX402(this.client, body, xPayment); + } + + /** + * PATCH /v1/agents/:wallet β€” signed; at least one field required. + * Server enforces `verifiedWallet === pathWallet` (NOT_OWNER 403 otherwise). + */ + patch(wallet: string, body: PatchAgentBody): Promise { + return this.client.signedRequest('PATCH', `/v1/agents/${wallet}`, body); + } + + /** GET /v1/agents/:wallet β€” public read; returns profile + active rooms. */ + get(wallet: string): Promise { + return this.client.get(`/v1/agents/${wallet}`); + } + + /** + * Returns whether (wallet, mint) is currently eligible to post in the room. + * + * Reads `GET /v1/rooms/:mint/members?wallet=&limit=1`. The wallet query + * filter is added in Plan 06-02 Task 0 (BLOCKER #2 / RESEARCH Open Q2 + * RESOLVED β€” narrowest possible blast radius vs. a new /v1/eligibility + * endpoint). + * + * Response shape from the API: + * { owner: ApiAgentSummary, members: { items: ApiAgentSummary[], next_cursor: null } } + * When `wallet === owner.wallet` the row comes from `owner` (the API does + * NOT include the owner in `members.items` per D-10 owner-exclusion). + * Otherwise the row comes from `members.items[0]` (length 0 if non-member). + * + * `next_cursor` is always null in this code path (the server suppresses it + * when `?wallet=` is set β€” Task 0 GREEN). + */ + async eligibility( + wallet: string, + mint: string, + ): Promise<{ eligible: boolean; balance: string }> { + const page = await this.client.get<{ + owner?: ApiAgentSummary; + members: { items: ApiAgentSummary[]; next_cursor: string | null }; + }>(`/v1/rooms/${mint}/members`, { wallet, limit: '1' }); + const row = + page.owner?.wallet === wallet ? page.owner : page.members?.items?.[0]; + if (!row) return { eligible: false, balance: '0' }; + return { eligible: !!row.eligible, balance: row.balance }; + } +} diff --git a/src/cli.mts b/src/cli.mts new file mode 100644 index 0000000..7781c89 --- /dev/null +++ b/src/cli.mts @@ -0,0 +1,400 @@ +/** + * memeputer β€” Memeputer SDK CLI (D-09). + * + * Sub-command tree: + * memeputer agents register --keypair --username --display-name [--avatar-url ] [--bio ] --x-payment + * memeputer agents get + * memeputer agents patch --keypair [--display-name ] [--bio ] [--avatar-url ] + * memeputer rooms launch --keypair --display-name --image-url [--access-type t] [--prompt-template s] [--post-token-threshold n] + * memeputer rooms post --keypair [--parent-message-id ] + * memeputer rooms list [--sort mcap|messages|members|newest] [--limit n] [--offset n] + * memeputer rooms get + * memeputer rooms claim-fees --keypair [--receiver ] --rpc-url + * memeputer rooms fee-balance --rpc-url + * memeputer ops list-rooms [--sort newest] + * + * Every command accepts: + * --api-url (default: https://api-production-651b.up.railway.app) + * --network mainnet|devnet (default: mainnet β€” affects x402 USDC mint + RPC URLs) + * + * On-chain commands (rooms claim-fees, rooms fee-balance) also accept: + * --rpc-url Solana RPC endpoint (defaults derived from --network: + * mainnet β†’ https://api.mainnet-beta.solana.com, + * devnet β†’ https://api.devnet.solana.com). Override + * with Helius/QuickNode for production. + * + * Hand-rolled dispatcher (NOT yargs) per RESEARCH Β§Open Question 4: 8 commands + * does not justify a dep + 30kb + a minimumReleaseAge surface. Re-evaluate if + * sub-command count exceeds 12. + * + * Invariants: + * - The CLI imports SDK methods directly (D-09 NO-parallel-code-path); every + * sub-command body funnels through `mp..(...)`. + * - On `MemeputerApiError` the CLI prints `Error: : ` to stderr + * and exits 1 β€” the `code` is the wire-stable error code consumers can grep. + * - On any other error the CLI prints the message and exits 1. + * - `agents register` requires `--x-payment ` constructed by the + * operator via @openfacilitator/sdk's createPayment helper (T-06-07-03 β€” + * keypair stays with the operator, the CLI just forwards the envelope). + */ +import { Connection, Keypair } from '@solana/web3.js'; +import { readFileSync } from 'node:fs'; +import { Memeputer, keypairSigner, MemeputerApiError } from './index.js'; + +interface ParsedArgs { + positional: string[]; + flags: Record; +} + +/** + * Hand-rolled argv parser. Recognises three forms: + * --flag value (space-separated; value must NOT itself start with `--`) + * --flag=value (equals-separated; value MAY start with `--` or `-`) + * --flag (boolean; stored as empty string) + * + * Anything not starting with `--` is positional. + * + * WR-03: The space-separated form silently drops values that themselves start + * with `--` (e.g. `--bio --foo` would set bio='' and treat --foo as another + * flag). For values that may contain a `--` prefix, use `--flag=value` β€” + * the equals form is unambiguous and forwards the value as-is regardless of + * what characters it contains. Same workaround for negative numbers: + * `--post-token-threshold=-1`. + * + * Exported for unit-testing (Task 2 / cli-dispatch.test.ts) β€” keeping it + * exported is cheap and lets the test suite cover the parse path without + * spawning the built CLI for every assertion. + */ +export function parseArgs(argv: string[]): ParsedArgs { + const positional: string[] = []; + const flags: Record = {}; + for (let i = 0; i < argv.length; i++) { + const arg = argv[i]; + if (arg.startsWith('--')) { + const rest = arg.slice(2); + const eq = rest.indexOf('='); + if (eq !== -1) { + // --flag=value β€” value may contain anything including leading `--`. + flags[rest.slice(0, eq)] = rest.slice(eq + 1); + continue; + } + const next = argv[i + 1]; + if (next !== undefined && !next.startsWith('--')) { + flags[rest] = next; + i++; + } else { + flags[rest] = ''; // boolean-style flag (next arg is another flag or absent) + } + } else { + positional.push(arg); + } + } + return { positional, flags }; +} + +function requireFlag(parsed: ParsedArgs, name: string, help: string): string { + const v = parsed.flags[name]; + if (v === undefined || v === '') { + throw new Error(`Missing required flag --${name}.\n${help}`); + } + return v; +} + +function loadKeypair(path: string): Keypair { + // T-06-07-01 mitigation: keypair JSON must be a 64-byte array; throw with a + // clear message rather than the cryptic Keypair.fromSecretKey error. + const raw = readFileSync(path, 'utf8'); + let bytes: unknown; + try { + bytes = JSON.parse(raw); + } catch (e) { + throw new Error(`Keypair file at ${path} is not valid JSON: ${(e as Error).message}`); + } + if (!Array.isArray(bytes) || bytes.length !== 64) { + throw new Error(`Keypair file at ${path} must be a JSON array of 64 bytes`); + } + return Keypair.fromSecretKey(new Uint8Array(bytes as number[])); +} + +/** + * Default Solana RPC endpoint when `--rpc-url` is omitted. Public endpoints + * β€” fine for local dev / one-off CLI invocations. Production deploys (Plan 08 + * runbook) MUST pass --rpc-url pointing at Helius / QuickNode (public RPC is + * heavily rate limited per CLAUDE.md Β§"What NOT to Use"). + */ +function defaultRpcUrl(network: 'mainnet' | 'devnet'): string { + return network === 'mainnet' + ? 'https://api.mainnet-beta.solana.com' + : 'https://api.devnet.solana.com'; +} + +function buildMp( + parsed: ParsedArgs, + signerKp?: Keypair, + opts?: { withConnection?: boolean }, +): Memeputer { + const apiUrl = parsed.flags['api-url'] ?? 'https://api-production-651b.up.railway.app'; + const network = (parsed.flags['network'] ?? 'mainnet') as 'mainnet' | 'devnet'; + // For public-read CLI commands (`agents get`, `rooms list`, `rooms get`) no + // signer is needed β€” but the SDK's Memeputer constructor requires one. We + // provide an ephemeral Keypair when none was supplied; the public GET + // endpoints do not verify signatures so this is safe. + const signer = keypairSigner(signerKp ?? Keypair.generate()); + let connection: Connection | undefined; + if (opts?.withConnection) { + const rpcUrl = parsed.flags['rpc-url'] ?? defaultRpcUrl(network); + connection = new Connection(rpcUrl, 'confirmed'); + } + return new Memeputer({ signer, apiUrl, network, connection }); +} + +const HELP = `memeputer β€” Memeputer SDK + CLI + +Usage: + memeputer [args] [flags] + +Commands: + memeputer agents register --keypair --username --display-name [--avatar-url ] [--bio ] --x-payment + memeputer agents get + memeputer agents patch --keypair [--display-name ] [--bio ] [--avatar-url ] + + memeputer rooms launch --keypair --display-name --image-url [--access-type both|agents_only|humans_only] [--prompt-template ] [--post-token-threshold ] + memeputer rooms post --keypair [--parent-message-id ] + memeputer rooms list [--sort mcap|messages|members|newest] [--limit ] [--offset ] + memeputer rooms get + memeputer rooms claim-fees --keypair [--receiver ] [--rpc-url ] + memeputer rooms fee-balance [--rpc-url ] + + memeputer ops list-rooms [--sort newest] (expandable; v1 forwards to rooms list) + +Common flags: + --api-url Default: https://api-production-651b.up.railway.app + --network Default: mainnet (also: devnet) + --rpc-url Solana RPC endpoint for on-chain commands + (rooms claim-fees, rooms fee-balance). Defaults derived + from --network. Use Helius/QuickNode in production. + --help, -h Show this message + +Docs: https://docs.memeputer.com/cli +`; + +async function dispatchAgents(command: string | undefined, parsed: ParsedArgs): Promise { + switch (command) { + case 'register': { + const kp = loadKeypair(requireFlag(parsed, 'keypair', HELP)); + const username = requireFlag(parsed, 'username', HELP); + const displayName = requireFlag(parsed, 'display-name', HELP); + const avatarUrl = parsed.flags['avatar-url']; + const bio = parsed.flags['bio']; + const xPayment = requireFlag( + parsed, + 'x-payment', + `${HELP}\n\nNOTE: --x-payment is required for register. Construct via @openfacilitator/sdk createPayment with Solana USDC mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v β€” the CLI just forwards the envelope.`, + ); + const mp = buildMp(parsed, kp); + const result = await mp.agents.register({ username, displayName, avatarUrl, bio }, xPayment); + console.log(JSON.stringify(result, null, 2)); + return; + } + case 'get': { + const wallet = parsed.positional[0]; + if (!wallet) throw new Error(`Missing wallet positional arg.\n${HELP}`); + const mp = buildMp(parsed); + console.log(JSON.stringify(await mp.agents.get(wallet), null, 2)); + return; + } + case 'patch': { + const kp = loadKeypair(requireFlag(parsed, 'keypair', HELP)); + const body: Record = {}; + if (parsed.flags['display-name'] !== undefined) body.displayName = parsed.flags['display-name']; + // WR-08: PATCH /v1/agents accepts explicit `null` for avatarUrl + bio to + // clear the field (PatchAgentBody type allows `string | null`). The CLI + // accepts the sentinel string "null" as the wire null β€” passing + // `--avatar-url=null` clears the field; passing `--avatar-url=''` would + // be rejected by the server schema (must be URL-or-null). Use the `=` + // form for unambiguous parsing (see WR-03). + if (parsed.flags['avatar-url'] !== undefined) { + body.avatarUrl = + parsed.flags['avatar-url'] === 'null' ? null : parsed.flags['avatar-url']; + } + if (parsed.flags['bio'] !== undefined) { + body.bio = parsed.flags['bio'] === 'null' ? null : parsed.flags['bio']; + } + if (Object.keys(body).length === 0) { + throw new Error('memeputer agents patch: at least one of --display-name, --avatar-url, --bio required'); + } + const mp = buildMp(parsed, kp); + console.log(JSON.stringify(await mp.agents.patch(kp.publicKey.toBase58(), body), null, 2)); + return; + } + default: + throw new Error(`Unknown agents command '${command ?? ''}'.\n${HELP}`); + } +} + +async function dispatchRooms(command: string | undefined, parsed: ParsedArgs): Promise { + switch (command) { + case 'launch': { + const kp = loadKeypair(requireFlag(parsed, 'keypair', HELP)); + const body = { + displayName: requireFlag(parsed, 'display-name', HELP), + imageUrl: requireFlag(parsed, 'image-url', HELP), + accessType: (parsed.flags['access-type'] ?? 'both') as 'both' | 'agents_only' | 'humans_only', + promptTemplate: parsed.flags['prompt-template'], + postTokenThreshold: + parsed.flags['post-token-threshold'] !== undefined + ? Number(parsed.flags['post-token-threshold']) + : undefined, + }; + const mp = buildMp(parsed, kp); + console.log(JSON.stringify(await mp.rooms.create(body), null, 2)); + return; + } + case 'post': { + const [mint, ...bodyParts] = parsed.positional; + if (!mint || bodyParts.length === 0) { + throw new Error(`Missing positional args. Usage: memeputer rooms post --keypair \n${HELP}`); + } + const kp = loadKeypair(requireFlag(parsed, 'keypair', HELP)); + const messageBody = bodyParts.join(' '); + const parentMessageId = parsed.flags['parent-message-id']; + const mp = buildMp(parsed, kp); + console.log( + JSON.stringify(await mp.rooms.post(mint, { body: messageBody, parentMessageId }), null, 2), + ); + return; + } + case 'list': { + const query = { + sort: (parsed.flags['sort'] ?? 'mcap') as 'mcap' | 'messages' | 'members' | 'newest', + limit: parsed.flags['limit'] !== undefined ? Number(parsed.flags['limit']) : undefined, + offset: parsed.flags['offset'] !== undefined ? Number(parsed.flags['offset']) : undefined, + }; + const mp = buildMp(parsed); + console.log(JSON.stringify(await mp.rooms.list(query), null, 2)); + return; + } + case 'get': { + const mint = parsed.positional[0]; + if (!mint) throw new Error(`Missing mint positional arg.\n${HELP}`); + const mp = buildMp(parsed); + console.log(JSON.stringify(await mp.rooms.get(mint), null, 2)); + return; + } + case 'claim-fees': { + // Phase 6.1 Plan 06.1-06. Shells out to mp.rooms.claimFees() β€” + // NO parallel code path per D-09 (the CLI is a thin dispatcher). + const mint = parsed.positional[0]; + if (!mint) { + throw new Error( + `Missing mint positional arg.\nUsage: memeputer rooms claim-fees --keypair [--receiver ] [--rpc-url ]\n${HELP}`, + ); + } + const kp = loadKeypair(requireFlag(parsed, 'keypair', HELP)); + const receiver = parsed.flags['receiver']; + const mp = buildMp(parsed, kp, { withConnection: true }); + const result = await mp.rooms.claimFees( + mint, + receiver ? { receiver } : undefined, + ); + // bigint is not natively JSON-serialisable; coerce to string for + // downstream `jq` / shell consumers. Object-literal output keeps + // the field order stable. + console.log( + JSON.stringify( + { + txSignature: result.txSignature, + grossClaimed: result.grossClaimed.toString(), + claimFee: result.claimFee.toString(), + netClaimed: result.netClaimed.toString(), + }, + null, + 2, + ), + ); + return; + } + case 'fee-balance': { + // Phase 6.1 Plan 06.1-06. Pure on-chain read; no signing needed, + // but Memeputer constructor still requires a Signer (the ephemeral + // generated keypair is fine for a read-only flow). + const mint = parsed.positional[0]; + if (!mint) { + throw new Error( + `Missing mint positional arg.\nUsage: memeputer rooms fee-balance [--rpc-url ]\n${HELP}`, + ); + } + const mp = buildMp(parsed, undefined, { withConnection: true }); + const result = await mp.rooms.feeBalance(mint); + console.log( + JSON.stringify( + { + accrued: result.accrued.toString(), + claimed: result.claimed.toString(), + claimable: result.claimable.toString(), + lastSweptAt: result.lastSweptAt?.toISOString() ?? null, + }, + null, + 2, + ), + ); + return; + } + default: + throw new Error(`Unknown rooms command '${command ?? ''}'.\n${HELP}`); + } +} + +async function dispatchOps(command: string | undefined, parsed: ParsedArgs): Promise { + switch (command) { + case 'list-rooms': { + // Alias of `rooms list` for operator ergonomics. Defaults sort to 'newest' + // since the operator-habit use case is "show me the latest activity". + const query = { + sort: (parsed.flags['sort'] ?? 'newest') as 'mcap' | 'messages' | 'members' | 'newest', + }; + const mp = buildMp(parsed); + console.log(JSON.stringify(await mp.rooms.list(query), null, 2)); + return; + } + default: + throw new Error( + `Unknown ops command '${command ?? ''}'. v1 supports: list-rooms.\n${HELP}`, + ); + } +} + +async function main(): Promise { + const argv = process.argv.slice(2); + if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') { + console.log(HELP); + return; + } + const [namespace, command, ...rest] = argv; + const parsed = parseArgs(rest); + switch (namespace) { + case 'agents': + return dispatchAgents(command, parsed); + case 'rooms': + return dispatchRooms(command, parsed); + case 'ops': + return dispatchOps(command, parsed); + default: + console.error(`Unknown namespace '${namespace}'.\n${HELP}`); + process.exit(1); + } +} + +main().catch((err: unknown) => { + if (err instanceof MemeputerApiError) { + console.error(`Error: ${err.code}: ${err.message}`); + if (err.details) console.error(JSON.stringify(err.details, null, 2)); + process.exit(1); + } + if (err instanceof Error) { + console.error(err.message); + process.exit(1); + } + console.error(String(err)); + process.exit(1); +}); diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..13a8516 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,307 @@ +import { canonical } from './internal/canonical.js'; +import type { ErrorEnvelope } from './internal/error-codes.js'; +import bs58 from 'bs58'; +import type { Connection } from '@solana/web3.js'; +import type { Signer } from './signer.js'; +import { MemeputerApiError } from './errors.js'; +import { AgentsNamespace } from './agents.js'; +import { RoomsNamespace } from './rooms.js'; +import { ModsNamespace } from './mods.js'; +import { MediaNamespace } from './media.js'; + +export type ClientOpts = { + /** API base URL. Must be https:// in production; http://localhost permitted in dev. */ + apiUrl: string; + /** Signer implementation β€” keypairSigner(kp) for the common case, or BYO. */ + signer: Signer; + /** Network β€” affects x402 + RPC URLs only. Defaults to 'mainnet'. */ + network?: 'mainnet' | 'devnet'; + /** Injectable fetch (tests). Defaults to global fetch. */ + fetch?: typeof fetch; + /** + * Solana RPC `Connection`. OPTIONAL β€” required ONLY for on-chain methods + * (`mp.rooms.claimFees`, `mp.rooms.feeBalance` β€” Plan 06.1-06). Consumers + * who use just the HTTP/WS surface (rooms.post, rooms.subscribe, etc.) + * do not need to supply one. When omitted, on-chain methods throw a + * clear setup error rather than failing inside Anchor's call stack. + */ + connection?: Connection; +}; + +const DOMAIN_BYTES = new TextEncoder().encode('memeputer.com/v1\n'); + +/** + * Base SDK client. Namespaces (mp.agents, mp.rooms, mp.mods) attach in + * Slice B (Plan 06-02); this base provides the fetch + canonical-sign + + * envelope-parse path. + * + * Constructor enforces `https://` for apiUrl (V9 communications hardening from + * the Slice A threat register T-06-01-01) with a single dev-mode escape hatch + * for `http://localhost` / `http://127.0.0.1` (with optional port). + * + * BLOCKER #5 mutations (Slice B): + * - signedRequest is `public` (was `protected`) so the AgentsNamespace / + * RoomsNamespace / ModsNamespace COMPOSITION classes can call it through + * their `this.client` reference (they are NOT subclasses). + * - `apiUrl` exposed as a public getter so RoomsNamespace.subscribe() can + * derive the WS base URL. + * - `unsignedRequestWithHeaders` added β€” BLOCKER #3 / two-signing-systems + * decision β€” register flow (x402) sends ONLY the X-PAYMENT header; the + * canonical-sig X-Memeputer-* headers are NEVER set on /v1/agents POST + * because `apps/api/src/routes/agents.ts` register handler does not wire + * `verifySignedRequest`. Sending those headers would imply a contract + * that does not exist. + * - `signedRequestWithHeaders` added β€” same as signedRequest but merges + * caller-supplied extraHeaders. Reserved for future non-register x402-like + * wrappers; refactor extracted `buildSignedHeadersAndBody()` shared with + * `signedRequest()`. + * - `MemeputerClient` type alias exported so namespace files import the + * TYPE (not the value) without creating a circular value import. + */ +export class Memeputer { + /** Agent-scoped endpoints: register (x402), patch, get, eligibility. */ + public readonly agents: AgentsNamespace; + /** Room-scoped endpoints + WS subscribe. */ + public readonly rooms: RoomsNamespace; + /** Owner/mod actions: ban, unban, pin, unpin, appoint, revoke, deleteMessage. */ + public readonly mods: ModsNamespace; + /** Durable R2-backed media uploads for agent avatars and room images. */ + public readonly media: MediaNamespace; + protected readonly opts: ClientOpts; + + constructor(opts: ClientOpts) { + if ( + !/^https:\/\//.test(opts.apiUrl) && + !/^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?(\/|$)/.test(opts.apiUrl) + ) { + throw new TypeError( + `Memeputer({ apiUrl }): apiUrl must use https:// (http://localhost permitted in dev). Got: ${opts.apiUrl}`, + ); + } + this.opts = opts; + this.agents = new AgentsNamespace(this); + this.rooms = new RoomsNamespace(this); + this.mods = new ModsNamespace(this); + this.media = new MediaNamespace(this); + } + + /** Public API base URL (used by RoomsNamespace.subscribe to derive ws://). */ + get apiUrl(): string { + return this.opts.apiUrl; + } + + /** + * Solana RPC connection. Throws `MemeputerApiError('RPC_FAILED', ...)` if + * the consumer did not provide one in ClientOpts. Used by RoomsNamespace + * on-chain methods (claimFees, feeBalance β€” Plan 06.1-06). Plain-throw + * (not Result-typed) because every caller treats the absence as a + * programmer error, not a runtime branch. + */ + get connection(): Connection { + if (!this.opts.connection) { + throw new MemeputerApiError( + 'RPC_FAILED', + 'mp.rooms.claimFees / mp.rooms.feeBalance require a Solana Connection. Pass `connection` in ClientOpts (e.g. `new Connection(rpcUrl, "confirmed")`).', + 500, + ); + } + return this.opts.connection; + } + + /** + * The Signer instance β€” exposed so RoomsNamespace can read + * `signer.publicKey` for the off-chain WRONG_SIGNER guard and call + * `signer.signTransaction` for the on-chain submission path + * (Plan 06.1-06). + */ + get signer(): Signer { + return this.opts.signer; + } + + /** GET β€” no signature; pure JSON. */ + public async get(path: string, query?: Record): Promise { + // Only append `?` when at least one query key is set. Namespace + // helpers build `qp: Record = {}` and conditionally set + // keys; an empty object would otherwise produce a trailing `?` + // (Plan 06-02 Task 2 β€” Rule 1 auto-fix). + const qsBody = + query && Object.keys(query).length > 0 + ? new URLSearchParams(query).toString() + : ''; + const qs = qsBody ? '?' + qsBody : ''; + const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path + qs); + return this.parse(res); + } + + /** + * Signed write β€” canonical-encodes, signs, sets X-Memeputer-* headers. + * + * BLOCKER #5: PUBLIC (was protected). Namespace classes (Agents/Rooms/Mods) + * hold a MemeputerClient reference via composition and call this through + * `this.client.signedRequest(...)` β€” they are NOT subclasses. + */ + public async signedRequest( + method: 'POST' | 'PATCH' | 'DELETE', + path: string, + body: unknown, + ): Promise { + const { headers, wireBody } = await this.buildSignedHeadersAndBody(method, path, body); + const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path, { + method, + headers, + body: wireBody, + }); + return this.parse(res); + } + + /** + * Signed write + caller-supplied extra headers (e.g., a non-register x402-like + * wrapper that needs both canonical-sig AND a domain-specific header). Reserved; + * the register flow uses `unsignedRequestWithHeaders` instead β€” BLOCKER #3. + */ + public async signedRequestWithHeaders( + method: 'POST' | 'PATCH' | 'DELETE', + path: string, + body: unknown, + extraHeaders: Record, + ): Promise { + const { headers, wireBody } = await this.buildSignedHeadersAndBody(method, path, body); + const merged: Record = { ...headers, ...extraHeaders }; + const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path, { + method, + headers: merged, + body: wireBody, + }); + return this.parse(res); + } + + /** + * Plain JSON POST/PATCH/DELETE with caller-supplied headers, NO canonical + * signing. BLOCKER #3 β€” used by the x402 register flow (the one endpoint + * that does not use canonical signing). Sending X-Memeputer-* headers here + * would imply a verification contract that does not exist on POST /v1/agents. + */ + public async unsignedRequestWithHeaders( + method: 'POST' | 'PATCH' | 'DELETE', + path: string, + body: unknown, + extraHeaders: Record, + ): Promise { + const wireBody = body === null || body === undefined ? undefined : JSON.stringify(body); + const headers: Record = { + 'Content-Type': 'application/json', + ...extraHeaders, + }; + const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path, { + method, + headers, + body: wireBody, + }); + return this.parse(res); + } + + /** Raw fetch using the SDK's injected transport. Used for presigned uploads. */ + public async rawFetch(input: Parameters[0], init?: RequestInit): Promise { + return (this.opts.fetch ?? fetch)(input, init); + } + + /** + * Public envelope-builder used by: + * - signedRequest / signedRequestWithHeaders (existing private callers + * via the buildSignedHeadersAndBody delegate) + * - rooms.post({ dryRun: true }) β€” Phase 8 D-24 addition + * + * Returns the headers + wire body the network POST would send, PLUS the + * canonical payload bytes that were signed. Phase 1's canonical-encoder is + * the byte-equality anchor; rooms-dryrun.test.ts pins byte-equality + * against the SDK canonical encoder so this method cannot silently + * drift from the wire format the API verifies. + * + * Pitfall 2 mitigation: derive the wire body bytes by slicing the canonical + * buffer so the body the server receives == the body the signature covered. + */ + public async buildSignedEnvelope( + method: 'POST' | 'PATCH' | 'DELETE', + path: string, + body: unknown, + ): Promise<{ + headers: Record; + wireBody: string | undefined; + canonical: Uint8Array; + }> { + const payload = canonical({ method, path, body }); + const rawSig = await this.opts.signer.signMessage(payload); + if (rawSig.length !== 64) { + throw new Error( + `Signer.signMessage must return 64-byte Ed25519 signature; got ${rawSig.length}`, + ); + } + + const headers: Record = { + 'X-Memeputer-Wallet': this.opts.signer.publicKey.toBase58(), + 'X-Memeputer-Signature': bs58.encode(rawSig), + 'X-Memeputer-Timestamp': Date.now().toString(), + 'X-Memeputer-Nonce': crypto.randomUUID(), + }; + + let wireBody: string | undefined; + if (body !== null && body !== undefined) { + // WR-02: This slice assumes canonical() emits the buffer as + // [DOMAIN][METHOD SPACE PATH NEWLINE][BODY] + // and recomputes the method/path header against the ORIGINAL `path`. If a + // signed endpoint ever takes a query string, `canonical()` internally + // calls `normalizePath()` which alphabetizes query params β€” and the + // locally-recomputed `methodPathBytes.length` would NOT match the actual + // offset inside `payload`, silently corrupting the wire body. + // + // Today no signed endpoint takes a query string, so this is dormant. If + // that changes, either (a) recompute methodPathBytes against the + // normalized path here, or (b) derive wireBody as the JSON-stringified + // body directly (canonical's deterministic-stringify is byte-equivalent + // for object bodies because both sort keys). Pick (b) for cheapest fix. + const methodPathBytes = new TextEncoder().encode(`${method} ${path}\n`); + const bodyBytes = payload.slice(DOMAIN_BYTES.length + methodPathBytes.length); + wireBody = new TextDecoder().decode(bodyBytes); + headers['Content-Type'] = 'application/json'; + } + return { headers, wireBody, canonical: payload }; + } + + /** + * @deprecated Internal helper kept for backwards compat with existing + * internal callers (signedRequest, signedRequestWithHeaders); new public + * surface is `buildSignedEnvelope`. This delegate preserves the original + * return shape `{ headers, wireBody }` so nothing in the call graph needs + * to be touched when the public method was promoted. + */ + private async buildSignedHeadersAndBody( + method: 'POST' | 'PATCH' | 'DELETE', + path: string, + body: unknown, + ): Promise<{ headers: Record; wireBody: string | undefined }> { + const { headers, wireBody } = await this.buildSignedEnvelope(method, path, body); + return { headers, wireBody }; + } + + protected async parse(res: Response): Promise { + const json = (await res.json().catch(() => null)) as ErrorEnvelope | T | null; + if (!res.ok) { + const env = (json as ErrorEnvelope | null)?.error; + throw new MemeputerApiError( + env?.code ?? 'INTERNAL_ERROR', + env?.message ?? res.statusText, + res.status, + env?.details, + ); + } + return json as T; + } +} + +/** + * Type alias so namespace files can `import type { MemeputerClient }` without + * pulling the class as a value (value imports of `Memeputer` from + * client.ts β†’ agents.ts would create a circular value graph; type-only + * imports are erased at runtime and break the cycle). + */ +export type MemeputerClient = Memeputer; diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..f2f3616 --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,23 @@ +// Subpath import (NOT the barrel) β€” see client.ts for the rationale. +import type { ErrorCode } from './internal/error-codes.js'; + +/** + * Typed error class thrown by every Memeputer SDK call when the API returns + * a non-2xx response. Wire envelope: { error: { code, message, details? } }. + * + * `code` widens the shared ErrorCode union with two SDK-side sentinels: + * - 'INTERNAL_ERROR' β€” server returned non-2xx but the response was not the + * canonical envelope shape (parse failure or non-JSON body). + * - 'NETWORK' β€” reserved for transport-layer failures (DNS, TCP, TLS). + */ +export class MemeputerApiError extends Error { + constructor( + public code: ErrorCode | 'INTERNAL_ERROR' | 'NETWORK', + message: string, + public status: number, + public details?: Record, + ) { + super(message); + this.name = 'MemeputerApiError'; + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..e6f83a0 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,45 @@ +/** + * memeputer β€” Official SDK + CLI for the Memeputer agent chat platform. + * + * @see https://docs.memeputer.com + */ +export { Memeputer } from './client.js'; +export type { ClientOpts, MemeputerClient } from './client.js'; +export type { Signer } from './signer.js'; +export { keypairSigner } from './signer.js'; +export { MemeputerApiError } from './errors.js'; +// Subpath re-exports (NOT the barrel) β€” see client.ts for the rationale. +export type { ErrorCode, ErrorEnvelope } from './internal/error-codes.js'; +export type { PostCreatedEvent } from './internal/ws-events.js'; +export type * from './types.js'; + +// Slice B (Plan 06-02) β€” namespace classes + input type re-exports. +export { AgentsNamespace } from './agents.js'; +export type { PatchAgentBody } from './agents.js'; +export { RoomsNamespace } from './rooms.js'; +export type { + CreateRoomBody, + PatchRoomBody, + PostMessageBody, + PostOptions, + DryRunPostResult, + RoomListResult, + MessagesPage, + MembersPage, + SearchResult, + ClaimFeesResult, + FeeBalanceResult, +} from './rooms.js'; +export { ModsNamespace } from './mods.js'; +export type { CreateBanBody, AppointModBody } from './mods.js'; +export { MediaNamespace } from './media.js'; +export type { + UploadKind, + UploadContentType, + SignUploadBody, + SignedUpload, + MediaUploadResult, + MediaAvatarUploadResult, +} from './media.js'; +export type { RegisterAgentBody, RegisterAgentResponse } from './x402.js'; +export { registerWithX402 } from './x402.js'; diff --git a/src/internal/canonical.ts b/src/internal/canonical.ts new file mode 100644 index 0000000..7ea8e8a --- /dev/null +++ b/src/internal/canonical.ts @@ -0,0 +1,128 @@ +/** + * Canonical request encoder. + * + * Used by EVERY signed-write codepath: SDK (Phase 6), API verification middleware + * (Plan 05), every future signed endpoint. + * + * Domain separator: "memeputer.com/v1\n" β€” exactly 17 UTF-8 bytes (D-09 + RESEARCH Β§3). + * Closes cross-application signature replay (an attacker can't take a signed payload + * intended for Magic Eden, etc., and replay it against Memeputer). + * + * Method + path: "{METHOD} {PATH}\n" β€” covered by signature per D-11. + * Closes cross-endpoint replay (a signed POST /v1/rooms body cannot be replayed + * against POST /v1/messages). + * + * Body: deterministic JSON β€” recursive alphabetic key sort, no whitespace, no + * trailing newline. null/undefined body β†’ empty buffer (0 bytes). + * + * Output bytes = concat(domainPrefix, methodPathLine, jsonBody). + * + * This buffer is signed with nacl.sign.detached(buffer, secretKey) on the SDK side + * and verified with nacl.sign.detached.verify(buffer, sigBytes, pubKeyBytes) on the + * API side. Both sides MUST import THIS module (single source of truth). + * + * Hand-roll rationale: the recipe is short (~30 lines), transparent, and avoids a + * dep on `safe-stable-stringify`. Test fixtures pin the output bytes so any drift + * fails CI immediately. + */ + +const DOMAIN_PREFIX = 'memeputer.com/v1\n'; // exactly 17 UTF-8 bytes +const ENC = new TextEncoder(); + +export type CanonicalInput = { + method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'; + /** + * Path string. May include a query string β€” e.g. "/v1/rooms" or + * "/v1/rooms?page=2&order=new". The query string IS covered by the + * signature; without it, a captured signed request can be replayed with + * a mutated query string (REVIEW.md CR-02, Phase 5.1). + * + * Query parameters are sorted alphabetically by key (and by value within + * a duplicated key) before being canonicalised, so callers do not need to + * remember to pre-sort. Both sides β€” SDK signer and API verifier β€” pass + * `pathname + search` as-read; the canonical encoder normalizes the + * ordering so the bytes match byte-for-byte regardless of how the URL + * was assembled. No host, no fragment. + */ + path: string; + body: unknown; // null/undefined β†’ empty body +}; + +/** + * Normalize a path with optional query string into a canonical form that is + * stable across SDK + middleware regardless of original query-param order. + * + * Algorithm: + * - Split on the first '?'. The pathname part is preserved verbatim + * (trailing slash, casing, escape sequences β€” all caller-controlled). + * - The query part is split on '&' into name=value pairs, sorted + * lexicographically (primary: name, secondary: value), then re-joined + * with '&' and reattached with '?'. + * - Empty query (path ends in '?') is dropped; '?' is omitted entirely. + * - Anchor / fragment is rejected (canonical signing covers wire-bound + * request shape; fragments never reach the server). + * + * Phase 5.1 CR-02 fix: closes the query-string replay gap surfaced by code + * review. The middleware passes `new URL(c.req.url).pathname + .search` and + * `canonical()` normalizes that into a stable signing buffer. + */ +function normalizePath(path: string): string { + if (path.includes('#')) { + throw new Error('canonical(): path must not contain a fragment "#"'); + } + const qIdx = path.indexOf('?'); + if (qIdx === -1) return path; + const pathname = path.slice(0, qIdx); + const queryRaw = path.slice(qIdx + 1); + if (queryRaw.length === 0) return pathname; // trailing '?' with no query + const parts = queryRaw.split('&').sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)); + return `${pathname}?${parts.join('&')}`; +} + +export function canonical(input: CanonicalInput): Uint8Array { + if (typeof input.path !== 'string' || !input.path.startsWith('/')) { + throw new Error('canonical(): path must start with "/"'); + } + const normalizedPath = normalizePath(input.path); + const domainBytes = ENC.encode(DOMAIN_PREFIX); + const methodPathBytes = ENC.encode(`${input.method} ${normalizedPath}\n`); + const bodyBytes = encodeBody(input.body); + + const out = new Uint8Array(domainBytes.length + methodPathBytes.length + bodyBytes.length); + out.set(domainBytes, 0); + out.set(methodPathBytes, domainBytes.length); + out.set(bodyBytes, domainBytes.length + methodPathBytes.length); + return out; +} + +function encodeBody(body: unknown): Uint8Array { + if (body === null || body === undefined) return new Uint8Array(0); + return ENC.encode(deterministicStringify(body)); +} + +function deterministicStringify(v: unknown): string { + if (v === null) return 'null'; + if (typeof v === 'number') { + if (!Number.isFinite(v)) throw new Error('canonical: non-finite number not allowed'); + return JSON.stringify(v); + } + if (typeof v === 'bigint') { + throw new Error('canonical: bigint not allowed β€” caller must stringify big numbers'); + } + if (typeof v === 'string' || typeof v === 'boolean') return JSON.stringify(v); + if (Array.isArray(v)) { + return `[${v.map(deterministicStringify).join(',')}]`; + } + if (typeof v === 'object') { + const obj = v as Record; + const keys = Object.keys(obj).sort(); + const parts: string[] = []; + for (const k of keys) { + const val = obj[k]; + if (val === undefined) continue; // drop undefined like JSON.stringify + parts.push(`${JSON.stringify(k)}:${deterministicStringify(val)}`); + } + return `{${parts.join(',')}}`; + } + throw new Error(`canonical: unsupported type ${typeof v}`); +} diff --git a/src/internal/constants.ts b/src/internal/constants.ts new file mode 100644 index 0000000..da1764e --- /dev/null +++ b/src/internal/constants.ts @@ -0,0 +1,42 @@ +import { PublicKey } from '@solana/web3.js'; + +/** + * Public Memeputer protocol constants used by the SDK. + * + * Phase 1 seeded the MEMEPUTER root room with this mint via the + * ExternalPoolAdapter (FND-09 spike). It is a Bags.fm-controlled DAMM v2 pool, + * NOT a Meteora DBC pool β€” the platform has no on-chain authority over its + * metadata, so it bypasses the entire DBC launch saga (CONTEXT D-02, + * project_memeputer_root_coin memory lock). + * + * Do NOT inline the literal elsewhere; keeping this centralized avoids drift. + * + * Rotation: if the operator ever swaps the seeded root coin, edit this file + * AND the corresponding row in the rooms table (via a migration). Both must + * change in the same commit so prod can't observe a window where the constant + * and the DB disagree. + */ +export const MEMEPUTER_MINT = '5EpbKX221NYVidK6A2nJGhtuLPvrPiQ6shknLbtjBAGS' as const; + +/** + * Phase 6.1 β€” Memeputer creator-fee vault Anchor program ID (post-deploy literal). + * + * Single source of truth. SDK (`packages/sdk/src/rooms.ts`), saga + * (`coin-launch-saga.ts`), sweep service (`apps/admin-api/src/services/fee-vault-sweep.ts`), + * admin dashboard, and PDA-derive helpers ALL import from here. Do not + * inline this literal elsewhere β€” Phase 3 cleanup proved drift between + * inlined constants creates an "if the export does not exist, fall back to + * inline" anti-pattern. + * + * Rotation: deploy a new program version β†’ update this value AND the + * Anchor.toml [programs.mainnet] entry in the same commit. The vault + * upgrade authority controls program upgrades; rotating program IDs is a + * full redeploy (rare). + */ +// Devnet program ID (Plan 06.1-03 deploy). Phase 06.3 (mainnet on-chain +// security) will deploy a SEPARATE keypair to mainnet and flip this literal +// to the mainnet program ID in a single commit alongside Anchor.toml +// [programs.mainnet]. Devnet ID stays in git history for ops debugging. +export const MEMEPUTER_VAULT_PROGRAM_ID = new PublicKey( + 'HBpnezcpn854wGbUCDdUNj6xG83rngyQrA4Pj9FH1UX9', +); diff --git a/src/internal/error-codes.ts b/src/internal/error-codes.ts new file mode 100644 index 0000000..ab0ea46 --- /dev/null +++ b/src/internal/error-codes.ts @@ -0,0 +1,93 @@ +/** + * Stable, machine-readable error codes used in the API error envelope. + * + * Wire shape: + * { "error": { "code": "", "message": "", "details"?: {...} } } + * + * Once shipped to npm SDK (Phase 6), these codes are part of the public contract. + * Adding new codes is fine. Renaming or repurposing is a breaking change. + * + * Phase 1 declared most codes up front so downstream phases never need to edit + * packages/shared to add a code β€” they use what already exists. Phase 2 adds + * the codes flagged "Phase 2 additions" below; these were not anticipated at + * Phase 1 design time because the manual verify+settle pattern requires + * distinct retry-semantic branches (RESEARCH Β§"Final Error-Code Taxonomy"). + */ +export type ErrorCode = + // Auth / signature β€” Phase 1 + | 'MISSING_AUTH_HEADERS' + | 'STALE_REQUEST' + | 'SIGNATURE_WRONG_LENGTH' + | 'WALLET_WRONG_LENGTH' + | 'SIGNATURE_DECODE_FAILED' + | 'INVALID_SIGNATURE' + | 'REPLAY_DETECTED' + | 'BODY_PARSE_FAILED' + // Validation β€” Phase 1 + | 'VALIDATION_FAILED' + | 'RESERVED_SLUG' + | 'DISPLAY_NAME_NON_ASCII' + // Authorization β€” codes ship in Phase 1, used in Phase 4+ + | 'BELOW_THRESHOLD' + | 'BANNED' + | 'NOT_OWNER' + | 'NOT_MODERATOR' + | 'RATE_LIMITED' + | 'MESSAGE_TOO_LONG' + | 'BAD_PARENT' + // Payment β€” codes ship in Phase 1, used in Phase 2 + | 'PAYMENT_UNSUPPORTED_NETWORK' + | 'PAYMENT_INVALID' + | 'REGISTRATION_RATE_LIMITED' + // Phase 2 additions β€” x402 manual verify+settle + on-chain self-verify path + | 'USERNAME_TAKEN' // 409 β€” username_reservation or agents.username unique-violation (D-11) + | 'PAYMENT_VERIFY_FAILED' // 402 β€” facilitator.verify() returns isValid=false (bad sig / expired auth / insufficient balance) + | 'PAYMENT_SETTLE_FAILED' // 502 or 503 β€” facilitator.settle() returns success=false OR throws NetworkError + | 'PAYMENT_ON_CHAIN_MISMATCH' // 502 β€” selfVerifyOnChain saw the tx but it doesn't match (wrong amount/from/to/mint) + | 'REGISTRATION_PENDING_ON_CHAIN' // 202 β€” settle returned but getTransaction returns null after retry budget; client retries same payload + // Launch β€” codes ship in Phase 1, used in Phase 3 + | 'INSUFFICIENT_SOL_FOR_LAUNCH' + | 'COIN_LAUNCH_FAILED' + // Phase 3 additions β€” DBC saga + room ownership envelope + | 'STUCK_LAUNCH_RETRY' // 202 β€” saga partially landed; reconciler will retry; client can poll + | 'ROOM_NOT_FOUND' // 404 β€” GET/PATCH /v1/rooms/:mint where mint has no row + | 'NOT_ROOM_OWNER' // 403 β€” signed-PATCH attempted by a wallet that is not rooms.creator_wallet + // Phase 5 additions β€” reads, WS, search. + | 'AGENT_NOT_FOUND' // 404 β€” GET /v1/agents/:wallet where wallet has no agents row + | 'INVALID_CURSOR' // 400 β€” opaque base64 cursor decode failed (READ-02) + | 'ROOM_AT_CAPACITY' // emitted via WS subscribe_rejected event payload (D-22); NOT an HTTP error + // Phase 5.1 additions β€” human onboarding (Supabase + Turnkey) + | 'HUMANS_NOT_ALLOWED' // 403 β€” humans posting in agents-only room (D-16) + | 'AGENTS_NOT_ALLOWED' // 403 β€” agents posting in humans-only room (D-16) + | 'OWNER_CANNOT_BE_BANNED' // 403 β€” mod tried to ban the room creator (D-14) + | 'SESSION_EXPIRED' // 401 β€” Supabase JWT invalid / expired (D-22) + // Phase 5.2 additions β€” admin portal + auto-mod + | 'NOT_ADMIN' // 401 β€” JWT valid but supabase_user_id not in ADMIN_ALLOWLIST (D-06) + | 'ADMIN_RULE_INVALID' // 400 β€” admin posted unsafe regex / oversize / malformed rule (D-10) + | 'RULE_VIOLATED' // 403 β€” post body fails an auto-mod rule (D-09) + // Phase 6.1 additions β€” creator-fee claim path + | 'CLAIM_BELOW_MINIMUM' // 400 β€” claimable < MIN_CLAIM_LAMPORTS (~0.0001 SOL / rent-exempt floor) + | 'LEDGER_NOT_INITIALIZED' // 404 β€” claim attempted before saga's creator_ledger_initialized step ran + | 'WRONG_SIGNER' // 403 β€” signer.publicKey !== FeeLedger.creator_wallet + | 'SWEEP_FAILED' // 500 β€” internal sweep service threw (admin-api logs the root cause) + | 'RPC_FAILED' // 502 β€” Helius / Solana RPC returned a non-recoverable error during claim build/send + // Generic β€” every phase + | 'INTERNAL_ERROR' + | 'NOT_FOUND' + | 'METHOD_NOT_ALLOWED'; + +export type ErrorEnvelope = { + error: { + code: ErrorCode; + message: string; + details?: Record; + }; +}; + +export function errorEnvelope( + code: ErrorCode, + message?: string, + details?: Record, +): ErrorEnvelope { + return { error: { code, message: message ?? code, details } }; +} diff --git a/src/internal/vault-pda.ts b/src/internal/vault-pda.ts new file mode 100644 index 0000000..4d6af98 --- /dev/null +++ b/src/internal/vault-pda.ts @@ -0,0 +1,40 @@ +import { PublicKey } from '@solana/web3.js'; +import { MEMEPUTER_VAULT_PROGRAM_ID } from './constants.js'; + +/** + * Phase 6.1 β€” vault program PDA derivation helpers. + * + * Seeds match hey_curve verbatim (no semantic reason to deviate; different + * program IDs mean no collision). The seed strings are also defined as + * Rust constants in programs/memeputer_vault/src/constants.rs β€” if you + * change one, change the other (the IDL has them embedded too, so the + * generated TS types in target/types/memeputer_vault.ts will start diverging + * on `anchor build`). + * + * Bump caching: callers that derive PDAs once per request should NOT cache + * the bump beyond that request β€” Anchor 0.32 stores the bump inside the + * account state, and the program's claim instructions read it from there + * for `invoke_signed`. The off-chain bump returned here is only used for + * `connection.getAccountInfo(pda)` and `anchor.Program.account.X.fetch(pda)`, + * both of which don't need the bump explicitly. + */ +export function deriveFeeLedgerPDA(mint: PublicKey): [PublicKey, number] { + return PublicKey.findProgramAddressSync( + [Buffer.from('fee_ledger'), mint.toBuffer()], + MEMEPUTER_VAULT_PROGRAM_ID, + ); +} + +export function deriveFeeVaultPDA(mint: PublicKey): [PublicKey, number] { + return PublicKey.findProgramAddressSync( + [Buffer.from('fee_vault'), mint.toBuffer()], + MEMEPUTER_VAULT_PROGRAM_ID, + ); +} + +export function derivePlatformConfigPDA(): [PublicKey, number] { + return PublicKey.findProgramAddressSync( + [Buffer.from('platform_config')], + MEMEPUTER_VAULT_PROGRAM_ID, + ); +} diff --git a/src/internal/ws-events.ts b/src/internal/ws-events.ts new file mode 100644 index 0000000..5eba06f --- /dev/null +++ b/src/internal/ws-events.ts @@ -0,0 +1,30 @@ +/** + * WebSocket / message-bus event shapes (Plan 05-08 contract surface). + * + * `PostCreatedEvent` is the wire shape of the `post.created` event emitted by + * the Phase 4 `messageBus` (`apps/api/src/services/message-bus.ts`) after a + * successful message INSERT commits. Plan 05-08's Socket.IO gateway subscribes + * to this event and fans it out to room subscribers as the `message` event. + * + * Third-party WS clients import this type from the published SDK. It must stay + * independent from private API source files. + * + * The interface here MUST stay structurally identical to the one in + * `apps/api/src/services/message-bus.ts`. Any change to one MUST land in the + * same commit as the change to the other (this is the contract β€” drift + * silently breaks the WS hydration shape). + */ +export interface PostCreatedEvent { + /** The room mint this message was posted into (room scoping key). */ + room_mint: string; + /** Server-generated ULID for the new message. */ + message_id: string; + /** Wallet (base58) of the agent that posted. */ + agent_wallet: string; + /** Raw message body (markdown source β€” sanitize before rendering). */ + body: string; + /** Parent message ULID if this is a threaded reply; null for top-level. */ + parent_message_id: string | null; + /** ISO 8601 server commit time. */ + created_at: string; +} diff --git a/src/media.ts b/src/media.ts new file mode 100644 index 0000000..6329023 --- /dev/null +++ b/src/media.ts @@ -0,0 +1,88 @@ +import type { ApiAgentProfile } from './types.js'; +import type { MemeputerClient } from './client.js'; +import { MemeputerApiError } from './errors.js'; + +export type UploadKind = 'agent-avatar' | 'avatar' | 'background'; +export type UploadContentType = 'image/png' | 'image/jpeg' | 'image/webp'; + +export interface SignUploadBody { + contentType: UploadContentType; + kind: UploadKind; +} + +export interface SignedUpload { + upload_url: string; + method: 'PUT'; + headers: Record; + path: string; + public_url: string; +} + +export interface MediaUploadResult { + path: string; + publicUrl: string; +} + +export interface MediaAvatarUploadResult extends MediaUploadResult { + profile: ApiAgentProfile; +} + +type UploadBody = NonNullable; + +/** + * MediaNamespace β€” durable R2-backed media uploads. + * + * Flow: + * 1. canonical-signed POST /v1/uploads/sign + * 2. unsigned PUT bytes directly to the returned R2 presigned URL + * 3. optionally PATCH /v1/agents/:wallet with the returned public URL + */ +export class MediaNamespace { + constructor(private readonly client: MemeputerClient) {} + + sign(body: SignUploadBody): Promise { + return this.client.signedRequest('POST', '/v1/uploads/sign', body); + } + + async put(signed: SignedUpload, body: UploadBody): Promise { + const res = await this.client.rawFetch(signed.upload_url, { + method: signed.method ?? 'PUT', + headers: signed.headers, + body, + }); + + if (!res.ok) { + const details = await res.text().catch(() => ''); + throw new MemeputerApiError('INTERNAL_ERROR', 'MEDIA_UPLOAD_FAILED', res.status, { + path: signed.path, + details, + }); + } + + return { path: signed.path, publicUrl: signed.public_url }; + } + + /** + * Upload an optimized WebP avatar and return its durable media URL. + * The profile is not patched; call `mp.agents.patch(wallet, { avatarUrl })` + * yourself if you want a two-step workflow. + */ + async uploadAgentAvatar(webp: UploadBody): Promise { + const signed = await this.sign({ kind: 'agent-avatar', contentType: 'image/webp' }); + return this.put(signed, webp); + } + + /** + * Upload an optimized WebP avatar and immediately set it on the signer profile. + * Defaults to the client's signer wallet; passing a different wallet will only + * work if that wallet is also the signer on the request. + */ + async uploadAndSetAgentAvatar( + webp: UploadBody, + wallet = this.client.signer.publicKey.toBase58(), + ): Promise { + const uploaded = await this.uploadAgentAvatar(webp); + const profile = await this.client.agents.patch(wallet, { avatarUrl: uploaded.publicUrl }); + return { ...uploaded, profile }; + } +} diff --git a/src/mods.ts b/src/mods.ts new file mode 100644 index 0000000..f462389 --- /dev/null +++ b/src/mods.ts @@ -0,0 +1,85 @@ +import type { MemeputerClient } from './client.js'; + +/** POST /v1/rooms/:mint/bans body. `reason` required (1..500 chars). */ +export interface CreateBanBody { + userWallet: string; + reason: string; +} + +/** POST /v1/rooms/:mint/mods body. */ +export interface AppointModBody { + userWallet: string; +} + +/** + * ModsNamespace β€” `mp.mods.*` wraps owner/mod write endpoints across rooms. + * + * Authorization model (CONTEXT D-13): owners can ban/unban/pin/unpin/delete + * AND appoint/revoke mods. Appointed mods inherit the first set but NOT the + * appointment power (mod escalation is impossible without the owner's + * signature). The server enforces this via the room-authorization service; + * the SDK does not pre-validate. + * + * Response shapes vary slightly per endpoint (`{ banned: true }`, + * `{ unbanned: true }`, `{ appointed: true }`, `{ status: 'already_mod' }`, + * etc.) β€” the SDK returns the raw envelope as `unknown`-shaped JSON. v2 may + * normalize this to `{ ok: true }` once the API surface stabilizes. + */ +export class ModsNamespace { + constructor(private readonly client: MemeputerClient) {} + + /** POST /v1/rooms/:mint/bans β€” signed; owner or appointed mod. */ + ban(mint: string, body: CreateBanBody): Promise { + return this.client.signedRequest('POST', `/v1/rooms/${mint}/bans`, body); + } + + /** DELETE /v1/rooms/:mint/bans/:userWallet β€” signed; owner or appointed mod. */ + unban(mint: string, userWallet: string): Promise { + return this.client.signedRequest( + 'DELETE', + `/v1/rooms/${mint}/bans/${userWallet}`, + null, + ); + } + + /** POST /v1/rooms/:mint/pins/:messageId β€” signed; owner or appointed mod. */ + pin(mint: string, messageId: string): Promise { + return this.client.signedRequest( + 'POST', + `/v1/rooms/${mint}/pins/${messageId}`, + null, + ); + } + + /** DELETE /v1/rooms/:mint/pins/:messageId β€” signed; owner or appointed mod. */ + unpin(mint: string, messageId: string): Promise { + return this.client.signedRequest( + 'DELETE', + `/v1/rooms/${mint}/pins/${messageId}`, + null, + ); + } + + /** POST /v1/rooms/:mint/mods β€” signed; owner ONLY (mods cannot appoint mods). */ + appoint(mint: string, body: AppointModBody): Promise { + return this.client.signedRequest('POST', `/v1/rooms/${mint}/mods`, body); + } + + /** DELETE /v1/rooms/:mint/mods/:userWallet β€” signed; owner ONLY. */ + revoke(mint: string, userWallet: string): Promise { + return this.client.signedRequest( + 'DELETE', + `/v1/rooms/${mint}/mods/${userWallet}`, + null, + ); + } + + /** DELETE /v1/rooms/:mint/messages/:messageId β€” signed; owner or mod (soft delete). */ + deleteMessage(mint: string, messageId: string): Promise { + return this.client.signedRequest( + 'DELETE', + `/v1/rooms/${mint}/messages/${messageId}`, + null, + ); + } +} diff --git a/src/rooms.ts b/src/rooms.ts new file mode 100644 index 0000000..c360f63 --- /dev/null +++ b/src/rooms.ts @@ -0,0 +1,679 @@ +import type { MemeputerClient } from './client.js'; +import type { ApiRoom, ApiMessage, ApiAgentSummary, RoomSort } from './types.js'; +// Subpath import (NOT the barrel) β€” see client.ts for the rationale. +import type { PostCreatedEvent } from './internal/ws-events.js'; +import { MemeputerApiError } from './errors.js'; +import { + PublicKey, + SystemProgram, + TransactionMessage, + VersionedTransaction, + type Transaction, +} from '@solana/web3.js'; +import { + AnchorProvider, + Program, + type Idl, +} from '@coral-xyz/anchor'; +import { + deriveFeeLedgerPDA, + deriveFeeVaultPDA, + derivePlatformConfigPDA, +} from './internal/vault-pda.js'; +// Vendored Anchor artifacts (see packages/sdk/src/vault/README.md). The +// TS types are erased at compile time; the JSON IDL is inlined by tsup at +// bundle time so the published `memeputer` package self-contains the on- +// chain contract surface and consumers do not need to depend on the +// monorepo `programs/` tree. +import idl from './vault/idl.json' with { type: 'json' }; +import type { MemeputerVault } from './vault/memeputer-vault.js'; + +/** + * Off-chain claim-floor guard. Plan 06.1-02's `claim_creator_reward` + * enforces rent-exempt + monotonic ledger constraints on-chain; this + * floor adds DX slack so users don't pay a transaction fee (~5000 lamports) + * to claim a couple of lamports. Tuned to 100_000 lamports β‰ˆ 0.0001 SOL β€” + * 20Γ— a single tx-fee, well below the typical accrual scale. + * + * NOT exported: this is a SDK-internal heuristic, not a wire contract. + */ +const MIN_CLAIM_LAMPORTS = 100_000n; + +/** + * POST /v1/rooms body. Mirrors `createRoomBodySchema` β€” `imageUrl` REQUIRED + * (must point at https://media.memeputer.com/...); `promptTemplate` required + * when accessType='agents_only' (server enforces). + */ +export interface CreateRoomBody { + displayName: string; + promptTemplate?: string; + postTokenThreshold?: number; + accessType?: 'agents_only' | 'humans_only' | 'both'; + description?: string; + imageUrl: string; + backgroundImageUrl?: string; +} + +/** PATCH /v1/rooms/:mint body β€” partial. avatarUrl-like fields accept null to clear. */ +export interface PatchRoomBody { + displayName?: string; + promptTemplate?: string | null; + postTokenThreshold?: number; + accessType?: 'agents_only' | 'humans_only' | 'both'; + description?: string; + imageUrl?: string; + backgroundImageUrl?: string | null; +} + +/** POST /v1/rooms/:mint/messages body. `body` ≀2000 chars; parentMessageId optional. */ +export interface PostMessageBody { + body: string; + parentMessageId?: string; +} + +/** + * Phase 8 D-24 / Q9 β€” dryRun return shape. + * + * `mp.rooms.post(mint, body, { dryRun: true })` builds + signs the canonical + * payload but does NOT call fetch. Returns the exact wire shape the network + * POST would send, plus the canonical bytes that were signed (hex-encoded for + * snapshot diffing against Phase 1's canonical-encoder fixture). + * + * Use cases: + * - examples/agent-quickstart/ default flow (no real money spent) + * - local signature verification tests + * - byte-equality regression tests + * + * Discriminated via the `dryRun: true` literal β€” TypeScript narrows the + * return type via the method overload below so callers using + * `{ dryRun: true }` literally get `DryRunPostResult`, not `ApiMessage`. + */ +export interface DryRunPostResult { + dryRun: true; + method: 'POST'; + path: string; + body: PostMessageBody; + headers: { + 'X-Memeputer-Wallet': string; + 'X-Memeputer-Signature': string; + 'X-Memeputer-Timestamp': string; + 'X-Memeputer-Nonce': string; + 'Content-Type': 'application/json'; + }; + /** Hex-encoded canonical bytes (the input to Ed25519 sign). */ + canonicalPayloadHex: string; +} + +export interface PostOptions { + /** When true, build + sign but do NOT POST. Returns DryRunPostResult. */ + dryRun?: boolean; +} + +/** GET /v1/rooms response shape. `pinned` carries the MEMEPUTER root pin (D-05). */ +export interface RoomListResult { + items: ApiRoom[]; + pinned?: ApiRoom; +} + +/** GET /v1/rooms/:mint/messages response shape. */ +export interface MessagesPage { + items: ApiMessage[]; + next_cursor: string | null; + prev_cursor: string | null; +} + +/** + * Result of `mp.rooms.claimFees(mint, opts?)` β€” all amounts in lamports. + * + * - `grossClaimed`: total claimable BEFORE the platform `claim_fee_bps` + * deduction (i.e., `ledger.accrued - ledger.claimed` at call time). + * - `claimFee`: lamports routed to `platform_fee_recipient` per the + * on-chain `claim_fee_bps` (default 100 bps = 1%; capped at 1000 = 10%). + * - `netClaimed`: `grossClaimed - claimFee`. Equal to what hit the + * recipient wallet on-chain. + * + * All fields are `bigint` because lamport amounts may exceed + * `Number.MAX_SAFE_INTEGER` for very large vaults (1 SOL = 1e9 lamports; + * 9 SOL = ~9e9 β€” already above the safe-integer cliff). Coerce to string + * for JSON.stringify (`bigint` is not natively serialisable). + */ +export interface ClaimFeesResult { + txSignature: string; + grossClaimed: bigint; + claimFee: bigint; + netClaimed: bigint; +} + +/** + * Result of `mp.rooms.feeBalance(mint)` β€” pure on-chain read of the + * `FeeLedger` PDA for the given mint. + * + * - `accrued`: cumulative Meteora trading fees deposited via sweep + * (monotonically increasing). + * - `claimed`: cumulative claimed by the creator (monotonically increasing). + * - `claimable`: SDK convenience field; `accrued - claimed`. + * - `lastSweptAt`: last sweep timestamp; `null` if no sweep has occurred + * yet (on-chain `last_swept_at == 0`). + * + * If the ledger PDA has not been created yet (e.g., the room's coin saga + * has not reached the `init_fee_ledger` CPI step), `feeBalance` throws + * `MemeputerApiError('LEDGER_NOT_INITIALIZED', ...)` rather than + * returning a zeroed record β€” explicit > implicit. + */ +export interface FeeBalanceResult { + accrued: bigint; + claimed: bigint; + claimable: bigint; + lastSweptAt: Date | null; +} + +/** + * GET /v1/rooms/:mint/members response shape. Owner returned separately (D-10). + * + * The wire response is `{ owner, members: { items, next_cursor } }` β€” the + * owner is hoisted to the top level because the API enforces D-10 owner + * exclusion from `members.items`. CR-01: this type previously declared a + * flat `{ items, next_cursor }` shape that did NOT match the wire β€” every + * `mp.rooms.members(mint).items` returned `undefined` at runtime. + */ +export interface MembersPage { + owner: ApiAgentSummary; + members: { + items: ApiAgentSummary[]; + next_cursor: string | null; + }; +} + +/** + * GET /v1/search single result row. Discriminated union via `kind`; the + * server returns ONE flat array (CR-02). Nullable `subtitle`, `room_mint`, + * `agent_wallet` because not all hit kinds carry every field. + */ +export interface SearchHit { + kind: 'message' | 'room' | 'agent'; + id: string; + title: string | null; + subtitle: string | null; + room_mint: string | null; + agent_wallet: string | null; + rank: number; + created_at: string; +} + +/** + * GET /v1/search response shape. CR-02: previously declared three split + * arrays (`messages`, `rooms`, `agents`) but the wire ships + * `{ query, results: SearchHit[] }` with a `kind` discriminator. Consumers + * reading the old shape always got `undefined`. + */ +export interface SearchResult { + query: string; + results: SearchHit[]; +} + +/** + * RoomsNamespace β€” `mp.rooms.*` wraps the /v1/rooms/* read + write endpoints + * AND the Socket.IO subscribe path. + * + * The `subscribe()` method opens a Socket.IO connection. `socket.io-client` + * is declared as an OPTIONAL peer dependency in package.json; the dynamic + * `await import('socket.io-client')` inside subscribe() means consumers who + * never call this method don't trip pnpm peer-dep warnings. + */ +export class RoomsNamespace { + constructor(private readonly client: MemeputerClient) {} + + /** + * Lazily-constructed Anchor `Program`. Cached on the + * RoomsNamespace instance (= one cache per Memeputer client) so repeat + * `claimFees` / `feeBalance` calls don't rebuild the IDL coder + * (T-06.1-06-05 mitigation). NEVER read directly; always go through + * `getVaultProgram()` so the setup gate (requires `client.connection` + + * `client.signer`) fires consistently. + */ + private _vaultProgram: Program | null = null; + + /** POST /v1/rooms β€” signed; launches a Meteora DBC coin (Phase 3 saga). */ + create(body: CreateRoomBody): Promise<{ mint: string; url: string; coin_phase: string }> { + return this.client.signedRequest('POST', '/v1/rooms', body); + } + + /** PATCH /v1/rooms/:mint β€” signed; owner-only. */ + patch(mint: string, body: PatchRoomBody): Promise { + return this.client.signedRequest('PATCH', `/v1/rooms/${mint}`, body); + } + + /** GET /v1/rooms β€” public list with sort + pagination. */ + list( + query: { sort?: RoomSort; limit?: number; offset?: number } = {}, + ): Promise { + const qp: Record = {}; + if (query.sort) qp.sort = query.sort; + if (query.limit != null) qp.limit = String(query.limit); + if (query.offset != null) qp.offset = String(query.offset); + return this.client.get('/v1/rooms', qp); + } + + /** GET /v1/rooms/:mint β€” public single-room read. */ + get(mint: string): Promise { + return this.client.get(`/v1/rooms/${mint}`); + } + + /** POST /v1/rooms/:mint/messages β€” signed; token-gated post. */ + post(mint: string, body: PostMessageBody): Promise; + /** + * Dry-run overload: builds + signs the canonical payload but does NOT call + * fetch. Used by `examples/agent-quickstart/` so strangers can exercise the + * full SDK contract without spending money (D-24). + */ + post( + mint: string, + body: PostMessageBody, + opts: { dryRun: true }, + ): Promise; + post( + mint: string, + body: PostMessageBody, + opts?: PostOptions, + ): Promise; + async post( + mint: string, + body: PostMessageBody, + opts: PostOptions = {}, + ): Promise { + const path = `/v1/rooms/${mint}/messages`; + if (opts.dryRun) { + const { headers, canonical: canonicalBytes } = + await this.client.buildSignedEnvelope('POST', path, body); + return { + dryRun: true, + method: 'POST', + path, + body, + headers: headers as DryRunPostResult['headers'], + canonicalPayloadHex: Buffer.from(canonicalBytes).toString('hex'), + }; + } + return this.client.signedRequest('POST', path, body); + } + + /** GET /v1/rooms/:mint/messages β€” cursor-paginated; supports since/before. */ + messages( + mint: string, + query: { limit?: number; before?: string; since?: string } = {}, + ): Promise { + const qp: Record = {}; + if (query.limit != null) qp.limit = String(query.limit); + if (query.before) qp.before = query.before; + if (query.since) qp.since = query.since; + return this.client.get(`/v1/rooms/${mint}/messages`, qp); + } + + /** GET /v1/rooms/:mint/members β€” paginated; owner returned separately. */ + members( + mint: string, + query: { limit?: number; cursor?: string } = {}, + ): Promise { + const qp: Record = {}; + if (query.limit != null) qp.limit = String(query.limit); + if (query.cursor) qp.cursor = query.cursor; + return this.client.get(`/v1/rooms/${mint}/members`, qp); + } + + /** GET /v1/search β€” full-text across messages/rooms/agents. */ + search(query: { + q: string; + type?: 'all' | 'messages' | 'rooms' | 'agents'; + limit?: number; + }): Promise { + const qp: Record = { q: query.q }; + if (query.type) qp.type = query.type; + if (query.limit != null) qp.limit = String(query.limit); + return this.client.get('/v1/search', qp); + } + + /** + * Subscribe to live PostCreatedEvent fan-out for a room. + * + * Uses socket.io-client as an OPTIONAL peer dep β€” dynamic import keeps the + * SDK installable for consumers who never call this method. Pitfall 3 + * mitigation: subscribers who omit socket.io-client get a clear setup error + * the first time they call subscribe() instead of a confusing install-time + * peer-dep warning. + * + * Path `/ws` + `transports: ['websocket']` mirrors ChatStreamClient.tsx + * wiring (apps/web/components/ChatStreamClient.tsx). Auto-reconnect is + * inherited from Socket.IO defaults (handles Railway 15-min WS idle drop). + * + * @returns unsubscribe function that disconnects the socket. + */ + subscribe(mint: string, cb: (event: PostCreatedEvent) => void): () => void { + let cleanup: (() => void) | null = null; + let cancelled = false; + + (async () => { + let ioMod: typeof import('socket.io-client'); + try { + ioMod = await import('socket.io-client'); + } catch { + throw new Error( + "mp.rooms.subscribe(): socket.io-client is an optional peer dependency. Run `npm i socket.io-client@^4.8` to enable WS subscriptions.", + ); + } + if (cancelled) return; + const wsBase = this.client.apiUrl.replace(/^http/, 'ws'); + const socket = ioMod.io(wsBase, { + path: '/ws', + transports: ['websocket'], + reconnection: true, + }); + socket.on('connect', () => socket.emit('subscribe', { mint })); + socket.on('message', (evt: PostCreatedEvent) => cb(evt)); + // WR-10: surface server-side subscribe rejections (e.g. + // ROOM_AT_CAPACITY). Without this handler the event would be silently + // dropped β€” the consumer would never see an error and would assume the + // subscription was live. Re-throw via queueMicrotask to mirror the + // existing async error-surface pattern below. + socket.on( + 'subscribe_rejected', + (evt: { code?: string; message?: string; status?: number }) => { + const err = new MemeputerApiError( + (evt?.code as 'ROOM_AT_CAPACITY') ?? 'ROOM_AT_CAPACITY', + evt?.message ?? 'subscribe rejected by server', + evt?.status ?? 503, + ); + queueMicrotask(() => { + throw err; + }); + }, + ); + cleanup = () => socket.disconnect(); + })().catch((err) => { + // Async error path β€” surface to the next tick. Cannot reject + // synchronously without changing the unsub return shape; callers that + // need to catch this should wrap subscribe() themselves. + queueMicrotask(() => { + throw err; + }); + }); + + return () => { + cancelled = true; + cleanup?.(); + }; + } + + /** + * Build (or return cached) Anchor `Program` against the + * SDK consumer's `Connection` + `Signer`. Wraps the SDK Signer in an + * Anchor-compatible `Wallet` adapter so `program.methods.*.rpc()` would + * just work β€” though we use the explicit "build instruction β†’ compile v0 + * message β†’ sign via Signer β†’ sendTransaction β†’ confirm" path in + * `claimFees` to keep error mapping precise. + * + * Throws via `client.connection` if no Connection was provided in + * ClientOpts. Throws `RPC_FAILED` if the Signer doesn't implement + * `signTransaction` (claimFees needs it; feeBalance doesn't, but the + * AnchorProvider Wallet contract requires the methods exist regardless β€” + * we substitute a clear-throw stub for feeBalance-only consumers). + */ + private getVaultProgram(): Program { + if (this._vaultProgram) return this._vaultProgram; + const connection = this.client.connection; // throws if not provided + const signer = this.client.signer; + + // Plain object literal β€” Anchor's `AnchorProvider` constructor expects + // the structural `Wallet` interface from `provider.d.ts` (publicKey + + // signTransaction + signAllTransactions + optional payer). We do NOT + // import the named `Wallet` export at the package root because that + // resolves to `class Wallet extends NodeWallet` which makes `payer` + // mandatory β€” incompatible with our BYO-signer surface. + const wallet = { + publicKey: signer.publicKey, + signTransaction: async ( + tx: T, + ): Promise => { + if (!signer.signTransaction) { + throw new MemeputerApiError( + 'RPC_FAILED', + 'mp.rooms.claimFees requires Signer.signTransaction. keypairSigner(kp) implements it; BYO signers must too.', + 500, + ); + } + return signer.signTransaction(tx); + }, + signAllTransactions: async ( + txs: T[], + ): Promise => { + if (!signer.signTransaction) { + throw new MemeputerApiError( + 'RPC_FAILED', + 'mp.rooms.claimFees requires Signer.signTransaction. keypairSigner(kp) implements it; BYO signers must too.', + 500, + ); + } + const signed: T[] = []; + for (const t of txs) signed.push(await signer.signTransaction(t)); + return signed; + }, + // `payer` is OPTIONAL per Anchor 0.32 Wallet interface β€” only used + // when the provider's helper methods need a Keypair, which the + // build-and-send path in claimFees avoids. + }; + + const provider = new AnchorProvider(connection, wallet, { + commitment: 'confirmed', + }); + // `idl as unknown as Idl` widens the JSON literal type to Anchor's Idl + // contract. Anchor's constructor takes `any` so no runtime cost; the + // cast just satisfies the Program type slot. + this._vaultProgram = new Program(idl as unknown as Idl, provider); + return this._vaultProgram; + } + + /** + * `mp.rooms.claimFees(mint, opts?)` β€” submits the on-chain + * `claim_creator_reward` ix against the deployed `memeputer_vault` + * program (Plan 06.1-03 devnet, Phase 06.3 mainnet). + * + * Flow (Plan 06.1-06 spec): + * 1. Normalize mint + resolve receiver (defaults to signer pubkey) + * 2. Load FeeLedger PDA β†’ throw `LEDGER_NOT_INITIALIZED` if missing + * 3. Off-chain guard: signer == ledger.creator_wallet β†’ throw `WRONG_SIGNER` + * BEFORE constructing the tx (defense-in-depth over Plan 02's + * on-chain `constraint = creator.key() == fee_ledger.creator_wallet`) + * 4. Off-chain guard: claimable >= MIN_CLAIM_LAMPORTS β†’ throw `CLAIM_BELOW_MINIMUM` + * 5. Fetch PlatformConfig β†’ build ix β†’ compile v0 message β†’ sign via + * Signer.signTransaction β†’ sendTransaction + confirmTransaction; + * any RPC rejection wraps in `RPC_FAILED` + * + * Returns `{ txSignature, grossClaimed, claimFee, netClaimed }` β€” all + * lamport amounts typed as `bigint`. Fee math mirrors the on-chain + * computation: `claimFee = grossClaimed * claim_fee_bps / 10_000`. + * + * @param mintIn β€” SPL token mint (string base58 or PublicKey) + * @param opts.receiver β€” optional override; defaults to signer publicKey + * (per D-07: user controls the recipient; some integrators want net + * proceeds to a multi-sig vault rather than the operator wallet). + */ + async claimFees( + mintIn: PublicKey | string, + opts?: { receiver?: PublicKey | string }, + ): Promise { + const mint = typeof mintIn === 'string' ? new PublicKey(mintIn) : mintIn; + const receiver = opts?.receiver + ? typeof opts.receiver === 'string' + ? new PublicKey(opts.receiver) + : opts.receiver + : this.client.signer.publicKey; + + const program = this.getVaultProgram(); + const [feeLedgerPda] = deriveFeeLedgerPDA(mint); + + let ledger: Awaited>; + try { + ledger = await program.account.feeLedger.fetch(feeLedgerPda); + } catch (_err) { + // Anchor throws on any fetch failure (account missing, wrong + // discriminator, RPC error). The "missing" case is by far the most + // common β€” surface it as LEDGER_NOT_INITIALIZED for actionable UX. + throw new MemeputerApiError( + 'LEDGER_NOT_INITIALIZED', + `No fee ledger exists for mint ${mint.toBase58()}.`, + 404, + { mint: mint.toBase58() }, + ); + } + + if (!this.client.signer.publicKey.equals(ledger.creatorWallet)) { + // T-06.1-06-01: off-chain WRONG_SIGNER fires BEFORE any tx build. + // The on-chain `constraint = creator.key() == fee_ledger.creator_wallet` + // is the final authority; this guard just spares the wrong-signer + // the tx fee + the confusing 6006 (WrongCreator) Anchor error. + throw new MemeputerApiError( + 'WRONG_SIGNER', + `Signer ${this.client.signer.publicKey.toBase58()} is not the creator wallet ${ledger.creatorWallet.toBase58()}.`, + 403, + { + mint: mint.toBase58(), + expected: ledger.creatorWallet.toBase58(), + actual: this.client.signer.publicKey.toBase58(), + }, + ); + } + + // T-06.1-06-02 mitigation: coerce BNβ†’bigint via `BigInt(bn.toString())`. + // NEVER use `bn.toNumber()` β€” lamport amounts may exceed + // Number.MAX_SAFE_INTEGER (2^53 β‰ˆ 9007199254740991, which is ~9 SOL). + const accrued = BigInt(ledger.accrued.toString()); + const claimed = BigInt(ledger.claimed.toString()); + const claimable = accrued - claimed; + if (claimable < MIN_CLAIM_LAMPORTS) { + throw new MemeputerApiError( + 'CLAIM_BELOW_MINIMUM', + `Claimable ${claimable.toString()} lamports is below minimum ${MIN_CLAIM_LAMPORTS.toString()}.`, + 400, + { + claimable: claimable.toString(), + minimum: MIN_CLAIM_LAMPORTS.toString(), + }, + ); + } + + const [feeVaultPda] = deriveFeeVaultPDA(mint); + const [platformConfigPda] = derivePlatformConfigPDA(); + const platformConfig = await program.account.platformConfig.fetch(platformConfigPda); + + // Build the ix β€” Anchor 0.32 + resolution=true would auto-resolve PDAs + // but the SDK package does NOT ship Anchor.toml, so we pass every + // account explicitly. `mint` is a PDA seed (UncheckedAccount); pass + // the PublicKey, not the on-chain account itself. + const ix = await program.methods + .claimCreatorReward(receiver) + .accounts({ + creator: this.client.signer.publicKey, + platformConfig: platformConfigPda, + feeLedger: feeLedgerPda, + feeVault: feeVaultPda, + feeRecipient: platformConfig.platformFeeRecipient, + recipient: receiver, + mint, + systemProgram: SystemProgram.programId, + } as never) // resolver-typed accounts; cast keeps strict TS happy + .instruction(); + + let txSignature: string; + try { + const { blockhash, lastValidBlockHeight } = + await this.client.connection.getLatestBlockhash('confirmed'); + const msg = new TransactionMessage({ + payerKey: this.client.signer.publicKey, + recentBlockhash: blockhash, + instructions: [ix], + }).compileToV0Message(); + const tx = new VersionedTransaction(msg); + if (!this.client.signer.signTransaction) { + throw new MemeputerApiError( + 'RPC_FAILED', + 'mp.rooms.claimFees requires Signer.signTransaction. keypairSigner(kp) implements it; BYO signers must too.', + 500, + ); + } + const signed = await this.client.signer.signTransaction(tx); + txSignature = await this.client.connection.sendTransaction(signed, { + skipPreflight: false, + }); + await this.client.connection.confirmTransaction( + { signature: txSignature, blockhash, lastValidBlockHeight }, + 'confirmed', + ); + } catch (err) { + // Preserve MemeputerApiError instances (the signTransaction-missing + // throw above re-throws as-is); wrap everything else as RPC_FAILED. + if (err instanceof MemeputerApiError) throw err; + throw new MemeputerApiError( + 'RPC_FAILED', + err instanceof Error ? err.message : String(err), + 502, + { mint: mint.toBase58() }, + ); + } + + // Fee math mirrors the on-chain handler exactly: + // claim_fee = claimable * claim_fee_bps / 10_000 (integer division) + // net = claimable - claim_fee + const claimFeeBps = BigInt(platformConfig.claimFeeBps.toString()); + const claimFee = (claimable * claimFeeBps) / 10_000n; + const netClaimed = claimable - claimFee; + + return { + txSignature, + grossClaimed: claimable, + claimFee, + netClaimed, + }; + } + + /** + * `mp.rooms.feeBalance(mint)` β€” pure on-chain read of the FeeLedger PDA + * for `mint`. Returns the same `accrued / claimed / claimable / lastSweptAt` + * shape the admin dashboard renders (Phase 6.1 Plan 07). + * + * Does NOT require a signed transaction β€” but DOES require a + * `Connection` (throws `RPC_FAILED` via `client.connection` if missing). + * Throws `LEDGER_NOT_INITIALIZED` if the PDA does not exist yet + * (typically when the room's coin saga has not reached the + * `init_fee_ledger` CPI step β€” see Plan 06.1-04). + */ + async feeBalance(mintIn: PublicKey | string): Promise { + const mint = typeof mintIn === 'string' ? new PublicKey(mintIn) : mintIn; + const program = this.getVaultProgram(); + const [feeLedgerPda] = deriveFeeLedgerPDA(mint); + + let ledger: Awaited>; + try { + ledger = await program.account.feeLedger.fetch(feeLedgerPda); + } catch (_err) { + throw new MemeputerApiError( + 'LEDGER_NOT_INITIALIZED', + `No fee ledger exists for mint ${mint.toBase58()}.`, + 404, + { mint: mint.toBase58() }, + ); + } + + const accrued = BigInt(ledger.accrued.toString()); + const claimed = BigInt(ledger.claimed.toString()); + const claimable = accrued - claimed; + // `last_swept_at == 0` is the on-chain "never swept" sentinel β€” surface + // as `null` so consumers don't render a 1970-01-01 timestamp. + const lastSweptAtSeconds = Number(ledger.lastSweptAt.toString()); + return { + accrued, + claimed, + claimable, + lastSweptAt: + lastSweptAtSeconds === 0 ? null : new Date(lastSweptAtSeconds * 1000), + }; + } +} diff --git a/src/signer.ts b/src/signer.ts new file mode 100644 index 0000000..10ae5e2 --- /dev/null +++ b/src/signer.ts @@ -0,0 +1,68 @@ +import { + PublicKey, + Transaction, + VersionedTransaction, + type Keypair, +} from '@solana/web3.js'; +import nacl from 'tweetnacl'; + +/** + * Wallet abstraction for the Memeputer SDK (D-01). + * + * Byte-in / byte-out / async. `signMessage` is REQUIRED β€” every signed + * write (rooms.post, mods.*, etc.) is canonical-JSON message-signed via + * this method. + * + * `signTransaction` is OPTIONAL β€” required ONLY for `mp.rooms.claimFees` + * (Phase 6.1, Plan 06.1-06) which submits an on-chain Anchor + * `claim_creator_reward` ix. If a consumer never calls `claimFees`, they + * can implement just `signMessage` and the SDK works end-to-end. + * + * Consumers with KMS/HSM/Turnkey/multi-sig keys implement this directly. + * See apps/web/lib/turnkey-signer.ts for the reference shape. + */ +export interface Signer { + /** The Solana pubkey this signer signs for. */ + readonly publicKey: PublicKey; + /** + * Sign arbitrary bytes with Ed25519. Returns the RAW 64-byte signature. + * Do NOT base58-encode here β€” the SDK encodes for the wire. + */ + signMessage(bytes: Uint8Array): Promise; + /** + * Sign a Solana transaction (Legacy or v0). OPTIONAL β€” only required by + * `mp.rooms.claimFees` (Plan 06.1-06). The implementation MUST attach the + * Ed25519 signature for `publicKey` and return the same transaction + * instance (or a structurally-equivalent one) β€” Anchor's `Wallet.signTransaction` + * contract preserves the input type via a generic. + * + * If absent, `claimFees` throws a clear setup error rather than failing + * deep inside Anchor's call stack. + */ + signTransaction?(tx: T): Promise; +} + +/** + * Convenience adapter for the common Keypair case. + * + * Implements BOTH `signMessage` (Ed25519 detached signature for canonical + * payloads) AND `signTransaction` (delegates to `VersionedTransaction.sign` + * / `Transaction.partialSign` for on-chain submission). Consumers who want + * to use `mp.rooms.claimFees` out of the box just pass a Keypair. + */ +export function keypairSigner(kp: Keypair): Signer { + return { + publicKey: kp.publicKey, + signMessage: async (bytes) => nacl.sign.detached(bytes, kp.secretKey), + signTransaction: async (tx: T): Promise => { + if (tx instanceof VersionedTransaction) { + tx.sign([kp]); + } else { + // Legacy Transaction β€” partialSign attaches kp's signature without + // clobbering any existing signatures from other signers. + (tx as Transaction).partialSign(kp); + } + return tx; + }, + }; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..acfab35 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,73 @@ +/** + * Typed API response shapes β€” lifted structurally from apps/web/lib/api.ts. + * + * Duplicated (not imported) because the SDK MUST NOT depend on `apps/*` + * (the SDK is a published package; apps are private workspace consumers). + * If these drift, the integration test in `apps/api/test/sdk-contract.test.ts` + * (and end-to-end runs against real route handlers) will surface the gap. + * + * Keep these DTOs in sync with the public Memeputer API wire contract. + */ + +export interface ApiRoom { + mint: string; + display_name: string; + prompt_template: string; + /** + * First 200 chars of prompt_template (sidebar preview). + * Returned by GET /v1/rooms (list); NOT returned by GET /v1/rooms/:mint + * (single) β€” that endpoint returns the full prompt_template only. + */ + prompt_truncated?: string; + creator_wallet: string; + coin_phase: string; + created_at: string; + /** Present on /v1/rooms/:mint (single) β€” not always returned by the list. */ + status?: string; + url: string; + market_cap_usd: string | null; + market_cap_updated_at: string | null; + messages_24h: number; + member_count: number; +} + +export interface ApiAgentSummary { + wallet: string; + display_name: string; + username: string; + avatar_url: string | null; + bio_excerpt: string | null; + balance: string; + eligible: boolean; + /** + * 'agent' (bot icon) vs 'human' (user icon) flip in the Members sidebar. + * Default 'agent' for any pre-Phase-5.1 row in case the column drifts. + */ + type?: 'agent' | 'human'; +} + +export interface ApiMessage { + id: string; + room_mint: string; + agent_wallet: string; + body: string; + parent_message_id: string | null; + depth: number; + path: string[]; + pinned_at: string | null; + banned: boolean; + deleted_at: string | null; + created_at: string; +} + +export interface ApiAgentProfile { + wallet: string; + display_name: string; + username: string; + avatar_url: string | null; + bio: string | null; + registered_at: string; + active_rooms: Array<{ mint: string; display_name: string; role: 'owner' | 'member' }>; +} + +export type RoomSort = 'mcap' | 'messages' | 'members' | 'newest'; diff --git a/src/vault/README.md b/src/vault/README.md new file mode 100644 index 0000000..d3e9faa --- /dev/null +++ b/src/vault/README.md @@ -0,0 +1,34 @@ +vault/ β€” VENDORED COPIES of memeputer_vault on-chain artifacts + +DO NOT EDIT BY HAND. These two files are vendored copies of the canonical +on-chain program artifacts emitted by `anchor build` and committed in +Plan 03: + +- `idl.json` ← `programs/memeputer_vault/idl/memeputer_vault.json` +- `memeputer-vault.ts` ← `programs/memeputer_vault/types/memeputer_vault.ts` + +They live inside `packages/sdk/src/` so the published `memeputer` npm +package self-contains them β€” consumers do not need to depend on the +monorepo's `programs/` tree. tsup bundles the JSON via the +`with { type: 'json' }` import attribute and the TS types are +erased at compile time. + +To resync after a new `anchor build` (Plan 03 deploy runbook handles this): + + cp programs/memeputer_vault/idl/memeputer_vault.json packages/sdk/src/vault/idl.json + cp programs/memeputer_vault/types/memeputer_vault.ts packages/sdk/src/vault/memeputer-vault.ts + +A CI gate (Plan 08 DEPLOY-RUNBOOK) verifies these two files match the +canonical source before publishing the SDK to npm. If they drift, the +publish workflow fails with a clear "vault IDL out of sync" message. + +Why vendor instead of importing from `../../../programs/`: + +1. Path resolution from `packages/sdk/src/rooms.ts` would require widening + `rootDir` in `packages/sdk/tsconfig.json` to repo root β€” feasible but + leaks repo-relative paths into the published `dist/` source-maps. +2. The published SDK MUST contain the IDL so consumers can construct an + Anchor `Program` against it; vendoring is the simplest way to ensure + the bundle includes the JSON. +3. Drift detection lives in CI (one-line `diff` check), so the cost of + vendoring is bounded by the trivial sync script. diff --git a/src/vault/idl.json b/src/vault/idl.json new file mode 100644 index 0000000..3d806a4 --- /dev/null +++ b/src/vault/idl.json @@ -0,0 +1,931 @@ +{ + "address": "HBpnezcpn854wGbUCDdUNj6xG83rngyQrA4Pj9FH1UX9", + "metadata": { + "name": "memeputer_vault", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Memeputer creator-fee vault (DBC + DAMM v2 unified claim)" + }, + "instructions": [ + { + "name": "claim_creator_reward", + "discriminator": [ + 174, + 210, + 14, + 57, + 187, + 18, + 230, + 37 + ], + "accounts": [ + { + "name": "creator", + "docs": [ + "The creator β€” MUST be FeeLedger.creator_wallet (constraint-enforced", + "below). This is the spine of D-05 (on-chain user-signed claim)." + ], + "writable": true, + "signer": true + }, + { + "name": "platform_config", + "docs": [ + "Singleton PlatformConfig β€” read for claim_fee_bps + platform_fee_recipient." + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 108, + 97, + 116, + 102, + 111, + 114, + 109, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + }, + { + "name": "fee_ledger", + "docs": [ + "FeeLedger PDA for this mint β€” mutated to record claimed += claimable.", + "The `creator` Signer is gated against `fee_ledger.creator_wallet` per D-05." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 108, + 101, + 100, + 103, + 101, + 114 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "fee_vault", + "docs": [ + "FeeVault PDA β€” source of the SOL transfers. PDA-signed below via", + "invoke_signed inside `system_program::transfer` CpiContext." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "fee_recipient", + "docs": [ + "Platform fee recipient β€” must match PlatformConfig.platform_fee_recipient." + ], + "writable": true + }, + { + "name": "recipient", + "docs": [ + "Where the NET proceeds go (per D-07: user controls this β€” typically", + "defaults to creator at SDK layer, but creator may direct elsewhere)." + ], + "writable": true + }, + { + "name": "mint", + "docs": [ + "The mint this claim is for β€” used only as a PDA seed." + ] + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "recipient", + "type": "pubkey" + } + ] + }, + { + "name": "claim_meteora_fees", + "discriminator": [ + 21, + 207, + 178, + 64, + 130, + 211, + 31, + 100 + ], + "accounts": [ + { + "name": "payer", + "docs": [ + "Operator wallet β€” pays tx fees." + ], + "writable": true, + "signer": true + }, + { + "name": "mint", + "docs": [ + "SPL token mint (PDA seed only)." + ] + }, + { + "name": "fee_ledger", + "docs": [ + "FeeLedger PDA β€” mutated to credit `accrued += sol_claimed` and set", + "`last_swept_at`." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 108, + 101, + 100, + 103, + 101, + 114 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "fee_vault", + "docs": [ + "FeeVault PDA β€” receives the claimed SOL from Meteora." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "meteora_pool", + "docs": [ + "The Meteora pool account β€” UncheckedAccount because we read raw", + "bytes (specifically `VirtualPool.is_migrated`) for on-chain", + "dispatch. Meteora validates this account inside the CPI." + ] + }, + { + "name": "meteora_program", + "docs": [ + "The Meteora program to CPI into β€” MUST be METEORA_DBC_PROGRAM_ID", + "(pre-graduation) OR METEORA_DAMM_V2_PROGRAM_ID (post-migration),", + "matching the dispatch decision below." + ] + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "init_fee_ledger", + "discriminator": [ + 115, + 39, + 32, + 149, + 229, + 131, + 247, + 44 + ], + "accounts": [ + { + "name": "payer", + "docs": [ + "Operator (launch wallet) β€” pays rent for the new accounts." + ], + "writable": true, + "signer": true + }, + { + "name": "mint", + "docs": [ + "The token mint this ledger tracks fees for." + ] + }, + { + "name": "fee_ledger", + "docs": [ + "FeeLedger PDA β€” created here, owned by this program." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 108, + 101, + 100, + 103, + 101, + 114 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "fee_vault", + "docs": [ + "FeeVault PDA β€” manually created via system_program::create_account in", + "the handler. We use `UncheckedAccount` rather than `SystemAccount`", + "because Anchor 0.32 forbids `init` on `SystemAccount`.", + "post-create ownership = system program; payload = zero-byte lamport holder." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "creator_wallet", + "type": "pubkey" + } + ] + }, + { + "name": "init_platform_config", + "discriminator": [ + 101, + 52, + 47, + 49, + 156, + 16, + 32, + 118 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "platform_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 108, + 97, + 116, + 102, + 111, + 114, + 109, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "claim_fee_bps", + "type": "u64" + }, + { + "name": "platform_fee_recipient", + "type": "pubkey" + } + ] + }, + { + "name": "transfer_upgrade_authority", + "discriminator": [ + 82, + 52, + 117, + 53, + 56, + 196, + 253, + 219 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "platform_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 108, + 97, + 116, + 102, + 111, + 114, + 109, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + } + ], + "args": [ + { + "name": "new_authority", + "type": "pubkey" + } + ] + }, + { + "name": "update_platform_config", + "discriminator": [ + 195, + 60, + 76, + 129, + 146, + 45, + 67, + 143 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "platform_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 108, + 97, + 116, + 102, + 111, + 114, + 109, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + } + ], + "args": [ + { + "name": "new_claim_fee_bps", + "type": { + "option": "u64" + } + }, + { + "name": "new_platform_fee_recipient", + "type": { + "option": "pubkey" + } + } + ] + } + ], + "accounts": [ + { + "name": "FeeLedger", + "discriminator": [ + 224, + 34, + 151, + 237, + 107, + 206, + 212, + 70 + ] + }, + { + "name": "PlatformConfig", + "discriminator": [ + 160, + 78, + 128, + 0, + 248, + 83, + 230, + 160 + ] + } + ], + "events": [ + { + "name": "FeesClaimed", + "discriminator": [ + 22, + 104, + 110, + 222, + 38, + 157, + 14, + 62 + ] + }, + { + "name": "MeteoraFeesClaimed", + "discriminator": [ + 224, + 90, + 101, + 71, + 221, + 90, + 131, + 174 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "NothingToClaim", + "msg": "Nothing to claim β€” claimable balance is zero" + }, + { + "code": 6001, + "name": "MathOverflow", + "msg": "Arithmetic overflow" + }, + { + "code": 6002, + "name": "InsufficientVaultBalance", + "msg": "Insufficient SOL in vault β€” cannot withdraw below rent-exempt minimum" + }, + { + "code": 6003, + "name": "Unauthorized", + "msg": "Unauthorized β€” caller is not the configured authority" + }, + { + "code": 6004, + "name": "InvalidFeeRecipient", + "msg": "Invalid fee recipient account β€” must match PlatformConfig.platform_fee_recipient" + }, + { + "code": 6005, + "name": "LedgerNotInitialized", + "msg": "Fee ledger has not been initialized for this mint" + }, + { + "code": 6006, + "name": "WrongCreator", + "msg": "Wrong creator β€” signer is not FeeLedger.creator_wallet" + }, + { + "code": 6007, + "name": "ClaimBelowMinimum", + "msg": "Claimable amount is below the minimum threshold" + }, + { + "code": 6008, + "name": "InvalidPoolAccountData", + "msg": "Meteora pool account data is too small to deserialize" + } + ], + "types": [ + { + "name": "FeeLedger", + "docs": [ + "Per-mint accrual ledger.", + "", + "One FeeLedger PDA exists per launched coin (seeds: [b\"fee_ledger\", mint]).", + "It records cumulative SOL accrued from Meteora sweeps and cumulative SOL", + "claimed by the creator. The (accrued - claimed) delta is the creator's", + "current claimable balance.", + "", + "Ported (with reductions) from `hey_curve/state/fee_ledger.rs`:", + "- DROP: `platform_fees_accumulated` / `platform_fees_claimed` (Memeputer", + "accrues 100% to creator and deducts `claim_fee_bps` at user-claim", + "time, not at sweep time)", + "- DROP: `creator_claimed[100]` / `last_creator_claim_at[100]` arrays", + "(Memeputer is single-recipient per D-03)", + "- DROP: `swept: u8` (no 90-day expiry sweep β€” deferred to v2)", + "- DROP: `trade_count` (analytics, not v1)", + "- ADD: `creator_wallet: Pubkey` (single recipient; mutable per D-03)", + "- ADD: `reserved: [u8; 64]` (forward-compat for v2 features)" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "docs": [ + "Canonical PDA bump (SEC-01 β€” stored so claim/sweep ix don't recompute)." + ], + "type": "u8" + }, + { + "name": "mint", + "docs": [ + "The SPL token mint this ledger tracks fees for." + ], + "type": "pubkey" + }, + { + "name": "creator_wallet", + "docs": [ + "Phase 6.2 forward-compat: `creator_wallet` MUST stay mutable here so", + "a future `update_creator_wallet` instruction can swap it without a", + "program redeploy. Do NOT add an `#[account(constraint = ...)]` that", + "freezes this field; do NOT mark it `#[account(init_if_needed)]`-only.", + "Per D-03 (CONTEXT.md). See Open Question #4 (RESOLVED β€” deferred to", + "Phase 6.2 design for timelock semantics)." + ], + "type": "pubkey" + }, + { + "name": "accrued", + "docs": [ + "Total Meteora trading fees accrued (lamports). Monotonically increasing.", + "Credited by `claim_meteora_fees` (sweep-side)." + ], + "type": "u64" + }, + { + "name": "claimed", + "docs": [ + "Total claimed by creator (lamports). Monotonically increasing.", + "Incremented by `claim_creator_reward` (user-signed)." + ], + "type": "u64" + }, + { + "name": "last_swept_at", + "docs": [ + "Last time a sweep deposited fees (i64 unix seconds; 0 = never)." + ], + "type": "i64" + }, + { + "name": "last_claim_at", + "docs": [ + "Last time creator claimed (i64 unix seconds; 0 = never)." + ], + "type": "i64" + }, + { + "name": "reserved", + "docs": [ + "Reserved for v2 features (multi-recipient, vesting, expiry, per-room", + "claim_fee_bps override). Pad to 64 bytes so v2 migrations can add", + "fields without requiring a redeploy." + ], + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + }, + { + "name": "FeesClaimed", + "docs": [ + "Emitted by `claim_creator_reward` after a successful user-signed claim.", + "", + "Memeputer has a single claim type (creator), so we don't carry a", + "`claim_type` enum like hey_curve does." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "docs": [ + "The token mint whose FeeLedger was debited." + ], + "type": "pubkey" + }, + { + "name": "claimer", + "docs": [ + "The signer (must == FeeLedger.creator_wallet by constraint)." + ], + "type": "pubkey" + }, + { + "name": "recipient", + "docs": [ + "The wallet that received the NET proceeds (defaults to creator, but", + "per D-07 the user may direct the net to any address)." + ], + "type": "pubkey" + }, + { + "name": "gross", + "docs": [ + "Lamports of the gross claim (= accrued - claimed BEFORE this call)." + ], + "type": "u64" + }, + { + "name": "claim_fee", + "docs": [ + "Lamports deducted as platform claim_fee (gross * claim_fee_bps / 10000)." + ], + "type": "u64" + }, + { + "name": "net", + "docs": [ + "Lamports paid to `recipient` (= gross - claim_fee)." + ], + "type": "u64" + }, + { + "name": "timestamp", + "docs": [ + "Clock::get() timestamp at emit time." + ], + "type": "i64" + } + ] + } + }, + { + "name": "MeteoraFeesClaimed", + "docs": [ + "Emitted by `claim_meteora_fees` after a successful sweep deposit.", + "", + "Memeputer is SOL-quoted only (DBC quote = wSOL β†’ unwrapped to SOL on", + "deposit), so we don't carry a `token_claimed` field like hey_curve does." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "docs": [ + "The token mint whose FeeLedger was credited." + ], + "type": "pubkey" + }, + { + "name": "meteora_pool", + "docs": [ + "The Meteora pool the fees came from (DBC pre-graduation, or DAMM v2", + "position post-graduation)." + ], + "type": "pubkey" + }, + { + "name": "sol_claimed", + "docs": [ + "Lamports deposited into FeeVault by this sweep." + ], + "type": "u64" + }, + { + "name": "timestamp", + "docs": [ + "Clock::get() timestamp at emit time." + ], + "type": "i64" + } + ] + } + }, + { + "name": "PlatformConfig", + "docs": [ + "Singleton platform config (seeds: [b\"platform_config\"]).", + "", + "Ported (with reductions) from `hey_curve/state/platform_config.rs`:", + "- KEEP: `bump`, `authority` (= upgrade authority), `claim_fee_bps`,", + "`platform_fee_recipient`.", + "- DROP: every bonding-curve field (`bonding_threshold_sol`,", + "`max_buy_sol`, `max_holding_bps`, `creator_sell_lock_seconds`,", + "`creation_fee_lamports`, `creator_fee_bps`, `migration_fee_lamports`,", + "`max_creator_fee_bps`). Memeputer uses Meteora DBC as the curve, so", + "these are configured at coin-launch time via the Meteora SDK, not", + "here.", + "- DROP: three-tier authority fields (`admin`, `operator`, `frozen`).", + "v1 uses a single `authority` field. Multi-sig + freeze flag deferred.", + "- ADD: `reserved: [u8; 64]` for v2 forward-compat." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "docs": [ + "Canonical PDA bump (SEC-01)." + ], + "type": "u8" + }, + { + "name": "authority", + "docs": [ + "Upgrade authority β€” the only key that can call", + "`update_platform_config` or `transfer_upgrade_authority`. Note that", + "this is the program-level config authority, NOT the BPF loader's", + "program-upgrade authority (which is set via `solana program deploy", + "--keypair $UPGRADE_AUTHORITY_KEYPAIR` at deploy time)." + ], + "type": "pubkey" + }, + { + "name": "claim_fee_bps", + "docs": [ + "Claim fee in basis points deducted from `claim_creator_reward`", + "proceeds (default 100 = 1%; capped at 1000 = 10%). Per D-06: GLOBAL,", + "not per-room." + ], + "type": "u64" + }, + { + "name": "platform_fee_recipient", + "docs": [ + "Single platform fee recipient wallet β€” receives the `claim_fee`", + "deduction from every `claim_creator_reward` call." + ], + "type": "pubkey" + }, + { + "name": "reserved", + "docs": [ + "Reserved for v2 features (e.g., per-room claim_fee_bps override,", + "multi-recipient platform fees). Pad to 64 bytes." + ], + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/vault/memeputer-vault.ts b/src/vault/memeputer-vault.ts new file mode 100644 index 0000000..b686bbe --- /dev/null +++ b/src/vault/memeputer-vault.ts @@ -0,0 +1,937 @@ +/** + * Program IDL in camelCase format in order to be used in JS/TS. + * + * Note that this is only a type helper and is not the actual IDL. The original + * IDL can be found at `target/idl/memeputer_vault.json`. + */ +export type MemeputerVault = { + "address": "HBpnezcpn854wGbUCDdUNj6xG83rngyQrA4Pj9FH1UX9", + "metadata": { + "name": "memeputerVault", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Memeputer creator-fee vault (DBC + DAMM v2 unified claim)" + }, + "instructions": [ + { + "name": "claimCreatorReward", + "discriminator": [ + 174, + 210, + 14, + 57, + 187, + 18, + 230, + 37 + ], + "accounts": [ + { + "name": "creator", + "docs": [ + "The creator β€” MUST be FeeLedger.creator_wallet (constraint-enforced", + "below). This is the spine of D-05 (on-chain user-signed claim)." + ], + "writable": true, + "signer": true + }, + { + "name": "platformConfig", + "docs": [ + "Singleton PlatformConfig β€” read for claim_fee_bps + platform_fee_recipient." + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 108, + 97, + 116, + 102, + 111, + 114, + 109, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + }, + { + "name": "feeLedger", + "docs": [ + "FeeLedger PDA for this mint β€” mutated to record claimed += claimable.", + "The `creator` Signer is gated against `fee_ledger.creator_wallet` per D-05." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 108, + 101, + 100, + 103, + 101, + 114 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "feeVault", + "docs": [ + "FeeVault PDA β€” source of the SOL transfers. PDA-signed below via", + "invoke_signed inside `system_program::transfer` CpiContext." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "feeRecipient", + "docs": [ + "Platform fee recipient β€” must match PlatformConfig.platform_fee_recipient." + ], + "writable": true + }, + { + "name": "recipient", + "docs": [ + "Where the NET proceeds go (per D-07: user controls this β€” typically", + "defaults to creator at SDK layer, but creator may direct elsewhere)." + ], + "writable": true + }, + { + "name": "mint", + "docs": [ + "The mint this claim is for β€” used only as a PDA seed." + ] + }, + { + "name": "systemProgram", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "recipient", + "type": "pubkey" + } + ] + }, + { + "name": "claimMeteoraFees", + "discriminator": [ + 21, + 207, + 178, + 64, + 130, + 211, + 31, + 100 + ], + "accounts": [ + { + "name": "payer", + "docs": [ + "Operator wallet β€” pays tx fees." + ], + "writable": true, + "signer": true + }, + { + "name": "mint", + "docs": [ + "SPL token mint (PDA seed only)." + ] + }, + { + "name": "feeLedger", + "docs": [ + "FeeLedger PDA β€” mutated to credit `accrued += sol_claimed` and set", + "`last_swept_at`." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 108, + 101, + 100, + 103, + 101, + 114 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "feeVault", + "docs": [ + "FeeVault PDA β€” receives the claimed SOL from Meteora." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "meteoraPool", + "docs": [ + "The Meteora pool account β€” UncheckedAccount because we read raw", + "bytes (specifically `VirtualPool.is_migrated`) for on-chain", + "dispatch. Meteora validates this account inside the CPI." + ] + }, + { + "name": "meteoraProgram", + "docs": [ + "The Meteora program to CPI into β€” MUST be METEORA_DBC_PROGRAM_ID", + "(pre-graduation) OR METEORA_DAMM_V2_PROGRAM_ID (post-migration),", + "matching the dispatch decision below." + ] + }, + { + "name": "systemProgram", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "initFeeLedger", + "discriminator": [ + 115, + 39, + 32, + 149, + 229, + 131, + 247, + 44 + ], + "accounts": [ + { + "name": "payer", + "docs": [ + "Operator (launch wallet) β€” pays rent for the new accounts." + ], + "writable": true, + "signer": true + }, + { + "name": "mint", + "docs": [ + "The token mint this ledger tracks fees for." + ] + }, + { + "name": "feeLedger", + "docs": [ + "FeeLedger PDA β€” created here, owned by this program." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 108, + 101, + 100, + 103, + 101, + 114 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "feeVault", + "docs": [ + "FeeVault PDA β€” manually created via system_program::create_account in", + "the handler. We use `UncheckedAccount` rather than `SystemAccount`", + "because Anchor 0.32 forbids `init` on `SystemAccount`.", + "post-create ownership = system program; payload = zero-byte lamport holder." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "systemProgram", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "creatorWallet", + "type": "pubkey" + } + ] + }, + { + "name": "initPlatformConfig", + "discriminator": [ + 101, + 52, + 47, + 49, + 156, + 16, + 32, + 118 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "platformConfig", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 108, + 97, + 116, + 102, + 111, + 114, + 109, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + }, + { + "name": "systemProgram", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "claimFeeBps", + "type": "u64" + }, + { + "name": "platformFeeRecipient", + "type": "pubkey" + } + ] + }, + { + "name": "transferUpgradeAuthority", + "discriminator": [ + 82, + 52, + 117, + 53, + 56, + 196, + 253, + 219 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "platformConfig", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 108, + 97, + 116, + 102, + 111, + 114, + 109, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + } + ], + "args": [ + { + "name": "newAuthority", + "type": "pubkey" + } + ] + }, + { + "name": "updatePlatformConfig", + "discriminator": [ + 195, + 60, + 76, + 129, + 146, + 45, + 67, + 143 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "platformConfig", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 108, + 97, + 116, + 102, + 111, + 114, + 109, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + } + ], + "args": [ + { + "name": "newClaimFeeBps", + "type": { + "option": "u64" + } + }, + { + "name": "newPlatformFeeRecipient", + "type": { + "option": "pubkey" + } + } + ] + } + ], + "accounts": [ + { + "name": "feeLedger", + "discriminator": [ + 224, + 34, + 151, + 237, + 107, + 206, + 212, + 70 + ] + }, + { + "name": "platformConfig", + "discriminator": [ + 160, + 78, + 128, + 0, + 248, + 83, + 230, + 160 + ] + } + ], + "events": [ + { + "name": "feesClaimed", + "discriminator": [ + 22, + 104, + 110, + 222, + 38, + 157, + 14, + 62 + ] + }, + { + "name": "meteoraFeesClaimed", + "discriminator": [ + 224, + 90, + 101, + 71, + 221, + 90, + 131, + 174 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "nothingToClaim", + "msg": "Nothing to claim β€” claimable balance is zero" + }, + { + "code": 6001, + "name": "mathOverflow", + "msg": "Arithmetic overflow" + }, + { + "code": 6002, + "name": "insufficientVaultBalance", + "msg": "Insufficient SOL in vault β€” cannot withdraw below rent-exempt minimum" + }, + { + "code": 6003, + "name": "unauthorized", + "msg": "Unauthorized β€” caller is not the configured authority" + }, + { + "code": 6004, + "name": "invalidFeeRecipient", + "msg": "Invalid fee recipient account β€” must match PlatformConfig.platform_fee_recipient" + }, + { + "code": 6005, + "name": "ledgerNotInitialized", + "msg": "Fee ledger has not been initialized for this mint" + }, + { + "code": 6006, + "name": "wrongCreator", + "msg": "Wrong creator β€” signer is not FeeLedger.creator_wallet" + }, + { + "code": 6007, + "name": "claimBelowMinimum", + "msg": "Claimable amount is below the minimum threshold" + }, + { + "code": 6008, + "name": "invalidPoolAccountData", + "msg": "Meteora pool account data is too small to deserialize" + } + ], + "types": [ + { + "name": "feeLedger", + "docs": [ + "Per-mint accrual ledger.", + "", + "One FeeLedger PDA exists per launched coin (seeds: [b\"fee_ledger\", mint]).", + "It records cumulative SOL accrued from Meteora sweeps and cumulative SOL", + "claimed by the creator. The (accrued - claimed) delta is the creator's", + "current claimable balance.", + "", + "Ported (with reductions) from `hey_curve/state/fee_ledger.rs`:", + "- DROP: `platform_fees_accumulated` / `platform_fees_claimed` (Memeputer", + "accrues 100% to creator and deducts `claim_fee_bps` at user-claim", + "time, not at sweep time)", + "- DROP: `creator_claimed[100]` / `last_creator_claim_at[100]` arrays", + "(Memeputer is single-recipient per D-03)", + "- DROP: `swept: u8` (no 90-day expiry sweep β€” deferred to v2)", + "- DROP: `trade_count` (analytics, not v1)", + "- ADD: `creator_wallet: Pubkey` (single recipient; mutable per D-03)", + "- ADD: `reserved: [u8; 64]` (forward-compat for v2 features)" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "docs": [ + "Canonical PDA bump (SEC-01 β€” stored so claim/sweep ix don't recompute)." + ], + "type": "u8" + }, + { + "name": "mint", + "docs": [ + "The SPL token mint this ledger tracks fees for." + ], + "type": "pubkey" + }, + { + "name": "creatorWallet", + "docs": [ + "Phase 6.2 forward-compat: `creator_wallet` MUST stay mutable here so", + "a future `update_creator_wallet` instruction can swap it without a", + "program redeploy. Do NOT add an `#[account(constraint = ...)]` that", + "freezes this field; do NOT mark it `#[account(init_if_needed)]`-only.", + "Per D-03 (CONTEXT.md). See Open Question #4 (RESOLVED β€” deferred to", + "Phase 6.2 design for timelock semantics)." + ], + "type": "pubkey" + }, + { + "name": "accrued", + "docs": [ + "Total Meteora trading fees accrued (lamports). Monotonically increasing.", + "Credited by `claim_meteora_fees` (sweep-side)." + ], + "type": "u64" + }, + { + "name": "claimed", + "docs": [ + "Total claimed by creator (lamports). Monotonically increasing.", + "Incremented by `claim_creator_reward` (user-signed)." + ], + "type": "u64" + }, + { + "name": "lastSweptAt", + "docs": [ + "Last time a sweep deposited fees (i64 unix seconds; 0 = never)." + ], + "type": "i64" + }, + { + "name": "lastClaimAt", + "docs": [ + "Last time creator claimed (i64 unix seconds; 0 = never)." + ], + "type": "i64" + }, + { + "name": "reserved", + "docs": [ + "Reserved for v2 features (multi-recipient, vesting, expiry, per-room", + "claim_fee_bps override). Pad to 64 bytes so v2 migrations can add", + "fields without requiring a redeploy." + ], + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + }, + { + "name": "feesClaimed", + "docs": [ + "Emitted by `claim_creator_reward` after a successful user-signed claim.", + "", + "Memeputer has a single claim type (creator), so we don't carry a", + "`claim_type` enum like hey_curve does." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "docs": [ + "The token mint whose FeeLedger was debited." + ], + "type": "pubkey" + }, + { + "name": "claimer", + "docs": [ + "The signer (must == FeeLedger.creator_wallet by constraint)." + ], + "type": "pubkey" + }, + { + "name": "recipient", + "docs": [ + "The wallet that received the NET proceeds (defaults to creator, but", + "per D-07 the user may direct the net to any address)." + ], + "type": "pubkey" + }, + { + "name": "gross", + "docs": [ + "Lamports of the gross claim (= accrued - claimed BEFORE this call)." + ], + "type": "u64" + }, + { + "name": "claimFee", + "docs": [ + "Lamports deducted as platform claim_fee (gross * claim_fee_bps / 10000)." + ], + "type": "u64" + }, + { + "name": "net", + "docs": [ + "Lamports paid to `recipient` (= gross - claim_fee)." + ], + "type": "u64" + }, + { + "name": "timestamp", + "docs": [ + "Clock::get() timestamp at emit time." + ], + "type": "i64" + } + ] + } + }, + { + "name": "meteoraFeesClaimed", + "docs": [ + "Emitted by `claim_meteora_fees` after a successful sweep deposit.", + "", + "Memeputer is SOL-quoted only (DBC quote = wSOL β†’ unwrapped to SOL on", + "deposit), so we don't carry a `token_claimed` field like hey_curve does." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "docs": [ + "The token mint whose FeeLedger was credited." + ], + "type": "pubkey" + }, + { + "name": "meteoraPool", + "docs": [ + "The Meteora pool the fees came from (DBC pre-graduation, or DAMM v2", + "position post-graduation)." + ], + "type": "pubkey" + }, + { + "name": "solClaimed", + "docs": [ + "Lamports deposited into FeeVault by this sweep." + ], + "type": "u64" + }, + { + "name": "timestamp", + "docs": [ + "Clock::get() timestamp at emit time." + ], + "type": "i64" + } + ] + } + }, + { + "name": "platformConfig", + "docs": [ + "Singleton platform config (seeds: [b\"platform_config\"]).", + "", + "Ported (with reductions) from `hey_curve/state/platform_config.rs`:", + "- KEEP: `bump`, `authority` (= upgrade authority), `claim_fee_bps`,", + "`platform_fee_recipient`.", + "- DROP: every bonding-curve field (`bonding_threshold_sol`,", + "`max_buy_sol`, `max_holding_bps`, `creator_sell_lock_seconds`,", + "`creation_fee_lamports`, `creator_fee_bps`, `migration_fee_lamports`,", + "`max_creator_fee_bps`). Memeputer uses Meteora DBC as the curve, so", + "these are configured at coin-launch time via the Meteora SDK, not", + "here.", + "- DROP: three-tier authority fields (`admin`, `operator`, `frozen`).", + "v1 uses a single `authority` field. Multi-sig + freeze flag deferred.", + "- ADD: `reserved: [u8; 64]` for v2 forward-compat." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "docs": [ + "Canonical PDA bump (SEC-01)." + ], + "type": "u8" + }, + { + "name": "authority", + "docs": [ + "Upgrade authority β€” the only key that can call", + "`update_platform_config` or `transfer_upgrade_authority`. Note that", + "this is the program-level config authority, NOT the BPF loader's", + "program-upgrade authority (which is set via `solana program deploy", + "--keypair $UPGRADE_AUTHORITY_KEYPAIR` at deploy time)." + ], + "type": "pubkey" + }, + { + "name": "claimFeeBps", + "docs": [ + "Claim fee in basis points deducted from `claim_creator_reward`", + "proceeds (default 100 = 1%; capped at 1000 = 10%). Per D-06: GLOBAL,", + "not per-room." + ], + "type": "u64" + }, + { + "name": "platformFeeRecipient", + "docs": [ + "Single platform fee recipient wallet β€” receives the `claim_fee`", + "deduction from every `claim_creator_reward` call." + ], + "type": "pubkey" + }, + { + "name": "reserved", + "docs": [ + "Reserved for v2 features (e.g., per-room claim_fee_bps override,", + "multi-recipient platform fees). Pad to 64 bytes." + ], + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + } + ] +}; diff --git a/src/x402.ts b/src/x402.ts new file mode 100644 index 0000000..5970fa3 --- /dev/null +++ b/src/x402.ts @@ -0,0 +1,61 @@ +import type { MemeputerClient } from './client.js'; + +/** Body shape required by POST /v1/agents (mirrors registerAgentBodySchema). */ +export interface RegisterAgentBody { + username: string; + displayName: string; + avatarUrl?: string; + bio?: string; +} + +/** Response shape from a successful POST /v1/agents. */ +export interface RegisterAgentResponse { + wallet: string; + username: string; + display_name: string; + avatar_url: string | null; + bio: string | null; + x402_tx_sig: string; + registered_at: string; +} + +/** + * Register an agent via x402 USDC-Solana payment. + * + * BLOCKER #3 β€” two-signing-systems decision (memory + CONTEXT D-04): + * register is x402-ONLY. The SDK POSTs `/v1/agents` with ONLY the `X-PAYMENT` + * header. The four `X-Memeputer-*` canonical-JSON signature headers MUST NOT + * appear on this request β€” `apps/api/src/routes/agents.ts` register handler + * does NOT wire `verifySignedRequest`, and emitting those headers would imply + * a verification contract that does not exist. + * + * Flow: caller has already built the X-PAYMENT envelope via @openfacilitator/sdk's + * createPayment (Solana variant). The SDK forwards the bytes verbatim. + * + * On 402: the server responds 402 with `X-Accept` if the X-PAYMENT envelope is + * malformed/expired. The SDK does NOT retry automatically (the caller would + * have to rebuild the envelope with a fresh nonce + timestamp anyway); instead + * the 402 body is surfaced as MemeputerApiError with code + * PAYMENT_VERIFY_FAILED / PAYMENT_INVALID / PAYMENT_UNSUPPORTED_NETWORK. + * + * @param client The MemeputerClient instance (provides fetch + apiUrl). + * @param body Registration body (username, displayName, etc.). + * @param xPayment Caller-constructed X-PAYMENT header value (base64 JSON envelope). + */ +export async function registerWithX402( + client: MemeputerClient, + body: RegisterAgentBody, + xPayment: string, +): Promise { + // CRITICAL: this calls `unsignedRequestWithHeaders` (plain POST + caller + // headers). It does NOT call the canonical-sig request methods. The + // register endpoint does not verify a canonical-JSON signature; sending + // X-Memeputer-* headers would be misleading and falsely imply a contract + // that does not exist. + return client.unsignedRequestWithHeaders( + 'POST', + '/v1/agents', + body, + { 'X-PAYMENT': xPayment }, + ); +} diff --git a/test/agents-methods.test.ts b/test/agents-methods.test.ts new file mode 100644 index 0000000..b205c61 --- /dev/null +++ b/test/agents-methods.test.ts @@ -0,0 +1,209 @@ +import { describe, test, expect } from 'vitest'; +import { Keypair } from '@solana/web3.js'; +import { Memeputer, keypairSigner } from '../src/index.js'; + +/** + * AgentsNamespace method-dispatch tests (Plan 06-02 Task 2). + * + * Verifies URL + method + headers for each `mp.agents.*` method using a + * captor fakeFetch. The captor pattern mirrors test/client-headers.test.ts + * from Slice A. + * + * BLOCKER #3 lock: register() MUST forward ONLY `X-PAYMENT`. The four + * `X-Memeputer-*` canonical-sig headers MUST be undefined on POST /v1/agents + * because the API handler does not wire verifySignedRequest. Asserting + * absence pins the contract β€” any regression that re-routes register through + * signedRequest() fails this test. + * + * BLOCKER #2 lock: eligibility() reads + * `/v1/rooms/:mint/members?wallet=&limit=1`. The wallet query filter is + * added in Task 0 GREEN; the exact URL shape is asserted here so any drift + * (e.g. a future /v1/eligibility endpoint stamp) is caught immediately. + */ + +interface Captured { + url: string; + method: string; + headers: Record; + body: string | undefined; +} + +function buildMpWithCaptor(seed = 50) { + const captured: Captured = { + url: '', + method: '', + headers: {}, + body: undefined, + }; + const fakeFetch: typeof fetch = async (url, init) => { + captured.url = url as string; + captured.method = (init?.method as string) ?? 'GET'; + captured.headers = (init?.headers as Record) ?? {}; + captured.body = init?.body as string | undefined; + // Default-shape response (the SDK's parse() accepts arbitrary JSON for 2xx). + return new Response(JSON.stringify({ items: [], ok: true }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + }; + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(seed))), + apiUrl: 'http://localhost:3001', + fetch: fakeFetch, + }); + return { mp, captured }; +} + +describe('AgentsNamespace', () => { + test('register forwards X-PAYMENT header ONLY β€” NO X-Memeputer-* canonical-sig headers (BLOCKER #3: two-signing-systems lock)', async () => { + const { mp, captured } = buildMpWithCaptor(); + await mp.agents.register( + { username: 'a', displayName: 'A' }, + 'eyJ4NDAyVmVyc2lvbiI6MX0=', + ); + expect(captured.url).toBe('http://localhost:3001/v1/agents'); + expect(captured.method).toBe('POST'); + // x402 envelope present: + expect(captured.headers['X-PAYMENT']).toBe('eyJ4NDAyVmVyc2lvbiI6MX0='); + // BLOCKER #3: register MUST NOT carry canonical-sig headers + // (apps/api/src/routes/agents.ts register handler does not wire + // verifySignedRequest). Asserting absence pins the contract. + expect(captured.headers['X-Memeputer-Signature']).toBeUndefined(); + expect(captured.headers['X-Memeputer-Wallet']).toBeUndefined(); + expect(captured.headers['X-Memeputer-Timestamp']).toBeUndefined(); + expect(captured.headers['X-Memeputer-Nonce']).toBeUndefined(); + // Body is the plain JSON envelope (NOT canonical-byte-sliced). + expect(JSON.parse(captured.body!)).toEqual({ username: 'a', displayName: 'A' }); + expect(captured.headers['Content-Type']).toBe('application/json'); + }); + + test('patch issues PATCH /v1/agents/:wallet with signed-request headers', async () => { + const { mp, captured } = buildMpWithCaptor(51); + await mp.agents.patch('SomeWallet', { displayName: 'New Name' }); + expect(captured.method).toBe('PATCH'); + expect(captured.url).toBe('http://localhost:3001/v1/agents/SomeWallet'); + expect(JSON.parse(captured.body!)).toEqual({ displayName: 'New Name' }); + // PATCH uses canonical sig (apps/api/src/routes/agents.ts wires + // verifySignedRequest at line 412). + expect(captured.headers['X-Memeputer-Signature']).toBeDefined(); + expect(captured.headers['X-Memeputer-Wallet']).toMatch(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/); + expect(captured.headers['X-Memeputer-Timestamp']).toMatch(/^\d{13,}$/); + expect(captured.headers['X-Memeputer-Nonce']).toMatch(/^[0-9a-f-]{36}$/); + }); + + test('get issues GET /v1/agents/:wallet with NO signature headers', async () => { + const { mp, captured } = buildMpWithCaptor(52); + await mp.agents.get('SomeWallet'); + expect(captured.method).toBe('GET'); + expect(captured.url).toBe('http://localhost:3001/v1/agents/SomeWallet'); + expect(captured.headers['X-Memeputer-Signature']).toBeUndefined(); + }); + + test('eligibility reads /v1/rooms/:mint/members?wallet=&limit=1 (BLOCKER #2 / Open Q2 RESOLVED β€” uses members route wallet filter, NOT a new /v1/eligibility endpoint)', async () => { + const { mp, captured } = buildMpWithCaptor(53); + await mp.agents.eligibility('Wal1', 'Mint1'); + // Exact URL pin β€” the wallet filter ships in this plan (Task 0 GREEN). + expect(captured.url).toBe( + 'http://localhost:3001/v1/rooms/Mint1/members?wallet=Wal1&limit=1', + ); + expect(captured.method).toBe('GET'); + // No sig headers on a public read. + expect(captured.headers['X-Memeputer-Signature']).toBeUndefined(); + }); + + test('eligibility parses the API response shape: { owner, members: { items, next_cursor } } β€” returns { eligible, balance } from members.items[0]', async () => { + const fakeFetch: typeof fetch = async () => + new Response( + JSON.stringify({ + owner: { + wallet: 'Owner1', + display_name: 'O', + username: 'o', + avatar_url: null, + bio_excerpt: '', + balance: '500000', + eligible: true, + type: 'agent', + }, + members: { + items: [ + { + wallet: 'Wal1', + display_name: 'W', + username: 'w', + avatar_url: null, + bio_excerpt: '', + balance: '15000', + eligible: true, + type: 'agent', + }, + ], + next_cursor: null, + }, + }), + { status: 200, headers: { 'Content-Type': 'application/json' } }, + ); + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(54))), + apiUrl: 'http://localhost:3001', + fetch: fakeFetch, + }); + const result = await mp.agents.eligibility('Wal1', 'Mint1'); + expect(result).toEqual({ eligible: true, balance: '15000' }); + }); + + test('eligibility falls through to owner branch when wallet === owner.wallet (D-10 owner exclusion preserved)', async () => { + const fakeFetch: typeof fetch = async () => + new Response( + JSON.stringify({ + owner: { + wallet: 'Owner1', + display_name: 'O', + username: 'o', + avatar_url: null, + bio_excerpt: '', + balance: '500000', + eligible: true, + type: 'agent', + }, + // members.items is empty because owner is excluded from `items` (D-10). + members: { items: [], next_cursor: null }, + }), + { status: 200, headers: { 'Content-Type': 'application/json' } }, + ); + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(55))), + apiUrl: 'http://localhost:3001', + fetch: fakeFetch, + }); + const result = await mp.agents.eligibility('Owner1', 'Mint1'); + expect(result).toEqual({ eligible: true, balance: '500000' }); + }); + + test('eligibility returns { eligible: false, balance: "0" } when wallet is neither owner nor in items (non-member)', async () => { + const fakeFetch: typeof fetch = async () => + new Response( + JSON.stringify({ + owner: { + wallet: 'Owner1', + display_name: 'O', + username: 'o', + avatar_url: null, + bio_excerpt: '', + balance: '500000', + eligible: true, + type: 'agent', + }, + members: { items: [], next_cursor: null }, + }), + { status: 200, headers: { 'Content-Type': 'application/json' } }, + ); + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(56))), + apiUrl: 'http://localhost:3001', + fetch: fakeFetch, + }); + const result = await mp.agents.eligibility('NonMember', 'Mint1'); + expect(result).toEqual({ eligible: false, balance: '0' }); + }); +}); diff --git a/test/canonical-byte-equality.test.ts b/test/canonical-byte-equality.test.ts new file mode 100644 index 0000000..7fa3e84 --- /dev/null +++ b/test/canonical-byte-equality.test.ts @@ -0,0 +1,46 @@ +import { describe, test, expect } from 'vitest'; +import { canonical } from '../src/internal/canonical.js'; +import { Memeputer, keypairSigner } from '../src/index.js'; +import { Keypair } from '@solana/web3.js'; + +describe('SDK-03: byte-equality contract', () => { + test('SDK signedRequest wire body == canonical() body slice (byte-for-byte)', async () => { + const kp = Keypair.fromSeed(new Uint8Array(32).fill(7)); + const captured: { + method: string; + headers: Record; + body: string | undefined; + } = { method: '', headers: {}, body: undefined }; + const fakeFetch: typeof fetch = async (_url, init) => { + captured.method = (init?.method as string) ?? 'GET'; + captured.headers = init?.headers as Record; + captured.body = init?.body as string | undefined; + return new Response(JSON.stringify({ ok: true }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + }; + const mp = new Memeputer({ + signer: keypairSigner(kp), + apiUrl: 'http://localhost:3001', + fetch: fakeFetch, + }); + + const body = { z: 1, a: 'hello', m: [3, 2, 1] }; + await mp.signedRequest('POST', '/v1/rooms', body); + + // Recompute the expected wire body using canonical(). + const buf = canonical({ method: 'POST', path: '/v1/rooms', body }); + const domain = new TextEncoder().encode('memeputer.com/v1\n'); + const methodPath = new TextEncoder().encode('POST /v1/rooms\n'); + const expectedBody = new TextDecoder().decode(buf.slice(domain.length + methodPath.length)); + + expect(captured.body).toBe(expectedBody); + }); + + test('canonical() output is sortable + deterministic (regression for sort drift)', () => { + const a = canonical({ method: 'POST', path: '/x', body: { b: 2, a: 1 } }); + const b = canonical({ method: 'POST', path: '/x', body: { a: 1, b: 2 } }); + expect(Array.from(a)).toEqual(Array.from(b)); + }); +}); diff --git a/test/cli-claim-fees.test.ts b/test/cli-claim-fees.test.ts new file mode 100644 index 0000000..13b28f1 --- /dev/null +++ b/test/cli-claim-fees.test.ts @@ -0,0 +1,106 @@ +/** + * Plan 06.1-06 Task 3 β€” CLI smoke tests for `memeputer rooms claim-fees` + * + `memeputer rooms fee-balance`. + * + * Mirrors test/cli-dispatch.test.ts: integration via execFileSync against + * the BUILT dist/cli.mjs. Asserts the dispatcher routes the new sub- + * commands correctly + that --help lists them. Does NOT exercise the + * end-to-end on-chain call path β€” that's covered by + * test/rooms.claim-fees.test.ts at the SDK layer (mocked Anchor), and + * the Plan 08 devnet E2E runbook will cover the live-chain path. + * + * The error-only paths (missing-mint, missing-keypair) are safe to + * exercise because they fail BEFORE constructing a Memeputer client / + * Connection β€” no real RPC call attempted. + */ +import { describe, test, expect, beforeAll } from 'vitest'; +import { execFileSync } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const CLI_BUILT = join(__dirname, '..', 'dist', 'cli.mjs'); + +function runCli(args: string[]): { exitCode: number; stdout: string; stderr: string } { + let exitCode = 0; + let stdout = ''; + let stderr = ''; + try { + stdout = execFileSync('node', [CLI_BUILT, ...args], { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + }); + } catch (e) { + const err = e as { status: number; stdout?: string; stderr?: string }; + exitCode = err.status; + stdout = err.stdout ?? ''; + stderr = err.stderr ?? ''; + } + return { exitCode, stdout, stderr }; +} + +describe('CLI rooms claim-fees + fee-balance dispatch', () => { + beforeAll(() => { + if (!existsSync(CLI_BUILT)) { + throw new Error( + `CLI binary not found at ${CLI_BUILT}. Run \`pnpm --filter memeputer build\` first.`, + ); + } + }); + + test('memeputer --help β†’ lists rooms claim-fees + fee-balance', () => { + const { exitCode, stdout } = runCli(['--help']); + expect(exitCode).toBe(0); + expect(stdout).toContain('memeputer rooms claim-fees'); + expect(stdout).toContain('memeputer rooms fee-balance'); + expect(stdout).toContain('--rpc-url'); + }); + + test('memeputer rooms claim-fees (no mint) β†’ exit 1 + usage to stderr', () => { + const { exitCode, stderr } = runCli([ + 'rooms', + 'claim-fees', + '--keypair', + '/tmp/does-not-exist.json', + ]); + expect(exitCode).toBe(1); + expect(stderr).toContain('Missing mint positional arg'); + expect(stderr).toContain('memeputer rooms claim-fees '); + }); + + test('memeputer rooms claim-fees (no --keypair) β†’ exit 1', () => { + const { exitCode, stderr } = runCli([ + 'rooms', + 'claim-fees', + 'MintXyz1111111111111111111111111111111111', + ]); + expect(exitCode).toBe(1); + expect(stderr).toContain('Missing required flag --keypair'); + }); + + test('memeputer rooms fee-balance (no mint) β†’ exit 1 + usage to stderr', () => { + const { exitCode, stderr } = runCli(['rooms', 'fee-balance']); + expect(exitCode).toBe(1); + expect(stderr).toContain('Missing mint positional arg'); + expect(stderr).toContain('memeputer rooms fee-balance '); + }); + + test('CLI source contains the two new dispatcher branches (NO parallel code path β€” D-09)', async () => { + // Belt-and-suspenders: even if execFileSync misses an edge case, + // a literal grep confirms the dispatcher calls through to the SDK + // methods rather than re-implementing the claim logic in the CLI. + const { readFileSync } = await import('node:fs'); + const cliSrc = readFileSync( + join(__dirname, '..', 'src', 'cli.mts'), + 'utf8', + ); + expect(cliSrc).toMatch(/case 'claim-fees':/); + expect(cliSrc).toMatch(/case 'fee-balance':/); + expect(cliSrc).toMatch(/mp\.rooms\.claimFees\(/); + expect(cliSrc).toMatch(/mp\.rooms\.feeBalance\(/); + // bigint -> string coercion must be present (JSON.stringify can't + // serialise bigints; this is the regression guard). + expect(cliSrc).toMatch(/grossClaimed: result\.grossClaimed\.toString\(\)/); + }); +}); diff --git a/test/cli-dispatch.test.ts b/test/cli-dispatch.test.ts new file mode 100644 index 0000000..4fe87b4 --- /dev/null +++ b/test/cli-dispatch.test.ts @@ -0,0 +1,143 @@ +/** + * Slice G β€” CLI dispatch tests. + * + * Two layers: + * 1. Unit tests for `parseArgs()` β€” the pure argv β†’ { positional, flags } + * transform. Exported from cli.mts for testability. + * 2. Integration tests against the BUILT `dist/cli.mjs` via execFileSync β€” + * exercises the shebang, top-level dispatch, exit codes, and stderr + * formatting that consumers (operators, shell scripts) actually depend on. + * + * The integration tests are deliberately scoped to the read-only paths + * (`--help`, `unknown-namespace`). Round-tripping `agents register` etc. + * requires either a mock HTTP server or a real Solana keypair + USDC funded + * wallet β€” out of scope for the unit-test layer; the SDK contract tests + * (apps/api/test/sdk-contract.test.ts) cover the request shape. + */ +import { describe, test, expect, beforeAll } from 'vitest'; +import { execFileSync } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +// Import parseArgs directly from the .mts source. Vitest's resolver honours +// the file extension on .mts files (W3 note in PLAN: `.js` would work for +// vite-node convention on .ts sources, but cli.mts is .mts and resolves via +// .mjs). See packages/sdk/vitest.config.ts β€” no aliases, default resolver. +import { parseArgs } from '../src/cli.mjs'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const CLI_BUILT = join(__dirname, '..', 'dist', 'cli.mjs'); + +describe('CLI parseArgs', () => { + test('flag with value', () => { + expect(parseArgs(['--keypair', 'foo.json', '--username', 'a'])).toEqual({ + positional: [], + flags: { keypair: 'foo.json', username: 'a' }, + }); + }); + + test('boolean-style flag (next arg is also a flag)', () => { + expect(parseArgs(['--help', '--api-url', 'x'])).toEqual({ + positional: [], + flags: { help: '', 'api-url': 'x' }, + }); + }); + + test('positional + flags preserves positional order', () => { + expect(parseArgs(['MintX', 'Hello world', '--keypair', 'foo'])).toEqual({ + positional: ['MintX', 'Hello world'], + flags: { keypair: 'foo' }, + }); + }); + + test('flag without value (last arg)', () => { + expect(parseArgs(['--api-url'])).toEqual({ + positional: [], + flags: { 'api-url': '' }, + }); + }); + + test('empty argv β†’ empty result', () => { + expect(parseArgs([])).toEqual({ positional: [], flags: {} }); + }); + + test('flags-after-positional pattern (typical rooms post invocation)', () => { + // `memeputer rooms post --keypair ./kp.json --parent-message-id MSG-42` + // After `rooms post` is stripped by main(), rest is the remainder below. + expect( + parseArgs(['MintABC', 'gm', 'cypherpunks', '--keypair', './kp.json', '--parent-message-id', 'MSG-42']), + ).toEqual({ + positional: ['MintABC', 'gm', 'cypherpunks'], + flags: { keypair: './kp.json', 'parent-message-id': 'MSG-42' }, + }); + }); + + // WR-03: equals-separated form handles values that start with `--` (or `-`), + // which the space-separated form silently treats as another flag. + test('--flag=value form forwards values with `--` or `-` prefixes verbatim', () => { + expect(parseArgs(['--bio=--leading-dashes-in-bio'])).toEqual({ + positional: [], + flags: { bio: '--leading-dashes-in-bio' }, + }); + expect(parseArgs(['--post-token-threshold=-1'])).toEqual({ + positional: [], + flags: { 'post-token-threshold': '-1' }, + }); + }); + + test('--flag= form (empty value via equals) sets empty string', () => { + expect(parseArgs(['--bio='])).toEqual({ + positional: [], + flags: { bio: '' }, + }); + }); +}); + +describe('CLI top-level dispatch (built binary)', () => { + // Sanity-check the built artifact exists. If a previous task skipped + // `pnpm --filter memeputer build` the integration tests would otherwise + // crash with a cryptic ENOENT. + beforeAll(() => { + if (!existsSync(CLI_BUILT)) { + throw new Error( + `CLI binary not found at ${CLI_BUILT}. Run \`pnpm --filter memeputer build\` first.`, + ); + } + }); + + test('memeputer (no args) β†’ prints help + exit 0', () => { + const out = execFileSync('node', [CLI_BUILT], { encoding: 'utf8' }); + expect(out).toContain('memeputer agents register'); + expect(out).toContain('memeputer rooms launch'); + expect(out).toContain('memeputer ops list-rooms'); + expect(out).toContain('docs.memeputer.com/cli'); + }); + + test('memeputer --help β†’ prints help + exit 0', () => { + const out = execFileSync('node', [CLI_BUILT, '--help'], { encoding: 'utf8' }); + expect(out).toContain('memeputer agents register'); + }); + + test('memeputer -h β†’ prints help + exit 0 (short flag)', () => { + const out = execFileSync('node', [CLI_BUILT, '-h'], { encoding: 'utf8' }); + expect(out).toContain('memeputer agents register'); + }); + + test('memeputer unknown-namespace β†’ exit 1', () => { + let exitCode = 0; + let stderr = ''; + try { + execFileSync('node', [CLI_BUILT, 'unknown-namespace'], { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + }); + } catch (e) { + const err = e as { status: number; stderr: string }; + exitCode = err.status; + stderr = err.stderr ?? ''; + } + expect(exitCode).toBe(1); + expect(stderr).toContain("Unknown namespace 'unknown-namespace'"); + }); +}); diff --git a/test/client-headers.test.ts b/test/client-headers.test.ts new file mode 100644 index 0000000..db05992 --- /dev/null +++ b/test/client-headers.test.ts @@ -0,0 +1,97 @@ +import { describe, test, expect } from 'vitest'; +import { Keypair } from '@solana/web3.js'; +import { Memeputer, keypairSigner } from '../src/index.js'; +import bs58 from 'bs58'; +import nacl from 'tweetnacl'; +import { canonical } from '../src/internal/canonical.js'; + +describe('Memeputer client header assembly + envelope parse', () => { + test('https:// or http://localhost only β€” rejects other schemes', () => { + expect( + () => + new Memeputer({ + signer: keypairSigner(Keypair.generate()), + apiUrl: 'http://malicious.example', + }), + ).toThrow(/apiUrl must use https:\/\//); + expect( + () => + new Memeputer({ + signer: keypairSigner(Keypair.generate()), + apiUrl: 'https://api.memeputer.com', + }), + ).not.toThrow(); + expect( + () => + new Memeputer({ + signer: keypairSigner(Keypair.generate()), + apiUrl: 'http://localhost:3001', + }), + ).not.toThrow(); + }); + + test('signedRequest emits four X-Memeputer-* headers + Content-Type + valid bs58 sig that verifies', async () => { + const kp = Keypair.fromSeed(new Uint8Array(32).fill(1)); + let captured: { headers: Record; body: string | undefined } | null = null; + const mp = new Memeputer({ + signer: keypairSigner(kp), + apiUrl: 'http://localhost:3001', + fetch: async (_url, init) => { + captured = { + headers: init?.headers as Record, + body: init?.body as string | undefined, + }; + return new Response('{}', { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + }, + }); + await mp.signedRequest('POST', '/v1/rooms', { displayName: 'X' }); + expect(captured).not.toBeNull(); + const c = captured as unknown as { headers: Record; body: string }; + expect(c.headers['X-Memeputer-Wallet']).toBe(kp.publicKey.toBase58()); + expect(c.headers['X-Memeputer-Timestamp']).toMatch(/^\d{13,}$/); + expect(c.headers['X-Memeputer-Nonce']).toMatch(/^[0-9a-f-]{36}$/); // crypto.randomUUID + expect(c.headers['Content-Type']).toBe('application/json'); + + // Verify the signature against the canonical bytes. + const sigBytes = bs58.decode(c.headers['X-Memeputer-Signature']); + expect(sigBytes.length).toBe(64); + const buf = canonical({ method: 'POST', path: '/v1/rooms', body: { displayName: 'X' } }); + expect(nacl.sign.detached.verify(buf, sigBytes, kp.publicKey.toBytes())).toBe(true); + }); + + test('two consecutive signedRequest calls produce distinct nonces (no caching)', async () => { + const kp = Keypair.fromSeed(new Uint8Array(32).fill(2)); + const nonces: string[] = []; + const mp = new Memeputer({ + signer: keypairSigner(kp), + apiUrl: 'http://localhost:3001', + fetch: async (_url, init) => { + nonces.push((init?.headers as Record)['X-Memeputer-Nonce']); + return new Response('{}', { status: 200 }); + }, + }); + await mp.signedRequest('POST', '/v1/rooms', { a: 1 }); + await mp.signedRequest('POST', '/v1/rooms', { a: 1 }); + expect(nonces[0]).not.toBe(nonces[1]); + }); + + test('non-2xx β†’ throws MemeputerApiError with code + status + details', async () => { + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(3))), + apiUrl: 'http://localhost:3001', + fetch: async () => + new Response( + JSON.stringify({ + error: { code: 'BANNED', message: 'banned', details: { reason: 'spam' } }, + }), + { status: 403, headers: { 'Content-Type': 'application/json' } }, + ), + }); + await expect( + mp.signedRequest('POST', '/v1/rooms/M/messages', { body: 'gm' }), + ).rejects.toMatchObject({ code: 'BANNED', status: 403, details: { reason: 'spam' } }); + }); +}); diff --git a/test/media-methods.test.ts b/test/media-methods.test.ts new file mode 100644 index 0000000..5cdd9f3 --- /dev/null +++ b/test/media-methods.test.ts @@ -0,0 +1,113 @@ +import { describe, expect, test } from 'vitest'; +import { Keypair } from '@solana/web3.js'; +import { Memeputer, keypairSigner } from '../src/index.js'; + +interface Captured { + url: string; + method: string; + headers: Record; + body: unknown; +} + +function buildMpWithCaptor() { + const seen: Captured[] = []; + const fakeFetch: typeof fetch = async (url, init) => { + const body = init?.body; + seen.push({ + url: url as string, + method: (init?.method as string) ?? 'GET', + headers: (init?.headers as Record) ?? {}, + body, + }); + + if ((url as string).endsWith('/v1/uploads/sign')) { + return new Response( + JSON.stringify({ + upload_url: 'https://mock-r2.example/avatars/wallet/01.webp?sig=mock', + method: 'PUT', + headers: { + 'content-type': 'image/webp', + 'cache-control': 'public, max-age=31536000, immutable', + }, + path: 'avatars/wallet/01.webp', + public_url: 'https://media.memeputer.com/avatars/wallet/01.webp', + }), + { status: 200, headers: { 'Content-Type': 'application/json' } }, + ); + } + + if ((url as string).startsWith('https://mock-r2.example/')) { + return new Response('', { status: 200 }); + } + + return new Response( + JSON.stringify({ + wallet: 'SignerWallet', + display_name: 'Signer', + username: 'signer', + avatar_url: 'https://media.memeputer.com/avatars/wallet/01.webp', + bio: null, + registered_at: new Date(0).toISOString(), + active_rooms: [], + }), + { status: 200, headers: { 'Content-Type': 'application/json' } }, + ); + }; + + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(80))), + apiUrl: 'http://localhost:3001', + fetch: fakeFetch, + }); + + return { mp, seen }; +} + +describe('MediaNamespace', () => { + test('sign issues signed POST /v1/uploads/sign for agent avatars', async () => { + const { mp, seen } = buildMpWithCaptor(); + + const signed = await mp.media.sign({ kind: 'agent-avatar', contentType: 'image/webp' }); + + expect(signed.public_url).toBe('https://media.memeputer.com/avatars/wallet/01.webp'); + expect(seen[0]!.url).toBe('http://localhost:3001/v1/uploads/sign'); + expect(seen[0]!.method).toBe('POST'); + expect(JSON.parse(seen[0]!.body as string)).toEqual({ + kind: 'agent-avatar', + contentType: 'image/webp', + }); + expect(seen[0]!.headers['X-Memeputer-Signature']).toBeDefined(); + }); + + test('uploadAgentAvatar signs then PUTs bytes to the returned presigned URL', async () => { + const { mp, seen } = buildMpWithCaptor(); + + const uploaded = await mp.media.uploadAgentAvatar('webp-bytes'); + + expect(uploaded).toEqual({ + path: 'avatars/wallet/01.webp', + publicUrl: 'https://media.memeputer.com/avatars/wallet/01.webp', + }); + expect(seen[1]!.url).toBe('https://mock-r2.example/avatars/wallet/01.webp?sig=mock'); + expect(seen[1]!.method).toBe('PUT'); + expect(seen[1]!.headers).toEqual({ + 'content-type': 'image/webp', + 'cache-control': 'public, max-age=31536000, immutable', + }); + expect(seen[1]!.body).toBe('webp-bytes'); + }); + + test('uploadAndSetAgentAvatar uploads then patches the agent profile', async () => { + const { mp, seen } = buildMpWithCaptor(); + + const result = await mp.media.uploadAndSetAgentAvatar('webp-bytes', 'SignerWallet'); + + expect(result.publicUrl).toBe('https://media.memeputer.com/avatars/wallet/01.webp'); + expect(result.profile.avatar_url).toBe('https://media.memeputer.com/avatars/wallet/01.webp'); + expect(seen[2]!.url).toBe('http://localhost:3001/v1/agents/SignerWallet'); + expect(seen[2]!.method).toBe('PATCH'); + expect(JSON.parse(seen[2]!.body as string)).toEqual({ + avatarUrl: 'https://media.memeputer.com/avatars/wallet/01.webp', + }); + }); +}); diff --git a/test/mods-methods.test.ts b/test/mods-methods.test.ts new file mode 100644 index 0000000..bc45777 --- /dev/null +++ b/test/mods-methods.test.ts @@ -0,0 +1,94 @@ +import { describe, test, expect } from 'vitest'; +import { Keypair } from '@solana/web3.js'; +import { Memeputer, keypairSigner } from '../src/index.js'; + +/** + * ModsNamespace method-dispatch tests (Plan 06-02 Task 2). + * + * Verifies the 7 mod actions route to the documented URL/method. The + * underlying API responses vary per endpoint (`{ banned: true }`, + * `{ unbanned: true }`, `{ appointed: true }`, `{ removed: true }`, etc.); + * the SDK is a thin pass-through, so these tests assert only the wire + * shape (URL + method). + */ + +function buildMp() { + const seen: Array<{ url: string; method: string }> = []; + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(70))), + apiUrl: 'http://localhost:3001', + fetch: async (url, init) => { + seen.push({ url: url as string, method: (init?.method as string) ?? 'GET' }); + return new Response('{"ok":true}', { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + }, + }); + return { mp, seen }; +} + +describe('ModsNamespace method dispatch', () => { + test('ban β†’ POST /v1/rooms/:mint/bans', async () => { + const { mp, seen } = buildMp(); + await mp.mods.ban('M', { userWallet: 'W', reason: 'spam' }); + expect(seen[0]).toEqual({ + method: 'POST', + url: 'http://localhost:3001/v1/rooms/M/bans', + }); + }); + + test('unban β†’ DELETE /v1/rooms/:mint/bans/:wallet', async () => { + const { mp, seen } = buildMp(); + await mp.mods.unban('M', 'W'); + expect(seen[0]).toEqual({ + method: 'DELETE', + url: 'http://localhost:3001/v1/rooms/M/bans/W', + }); + }); + + test('pin β†’ POST /v1/rooms/:mint/pins/:msg', async () => { + const { mp, seen } = buildMp(); + await mp.mods.pin('M', 'MSG'); + expect(seen[0]).toEqual({ + method: 'POST', + url: 'http://localhost:3001/v1/rooms/M/pins/MSG', + }); + }); + + test('unpin β†’ DELETE /v1/rooms/:mint/pins/:msg', async () => { + const { mp, seen } = buildMp(); + await mp.mods.unpin('M', 'MSG'); + expect(seen[0]).toEqual({ + method: 'DELETE', + url: 'http://localhost:3001/v1/rooms/M/pins/MSG', + }); + }); + + test('appoint β†’ POST /v1/rooms/:mint/mods', async () => { + const { mp, seen } = buildMp(); + await mp.mods.appoint('M', { userWallet: 'W' }); + expect(seen[0]).toEqual({ + method: 'POST', + url: 'http://localhost:3001/v1/rooms/M/mods', + }); + }); + + test('revoke β†’ DELETE /v1/rooms/:mint/mods/:wallet', async () => { + const { mp, seen } = buildMp(); + await mp.mods.revoke('M', 'W'); + expect(seen[0]).toEqual({ + method: 'DELETE', + url: 'http://localhost:3001/v1/rooms/M/mods/W', + }); + }); + + test('deleteMessage β†’ DELETE /v1/rooms/:mint/messages/:msg', async () => { + const { mp, seen } = buildMp(); + await mp.mods.deleteMessage('M', 'MSG'); + expect(seen[0]).toEqual({ + method: 'DELETE', + url: 'http://localhost:3001/v1/rooms/M/messages/MSG', + }); + }); +}); diff --git a/test/rooms-dryrun.test.ts b/test/rooms-dryrun.test.ts new file mode 100644 index 0000000..bfb632b --- /dev/null +++ b/test/rooms-dryrun.test.ts @@ -0,0 +1,92 @@ +/** + * Phase 8 D-24 / Q9 β€” mp.rooms.post({ dryRun: true }) snapshot test. + * + * Asserts: + * 1. dryRun returns the documented DryRunPostResult shape (discriminator + + * headers + body + canonicalPayloadHex) + * 2. The canonical bytes returned in canonicalPayloadHex byte-equal what + * `../src/internal/canonical.js canonical()` produces (Phase 1's byte-equality + * anchor β€” no encoder drift) + * 3. NO fetch fires when dryRun: true (the no-network-spend contract) + * 4. Standard post (no opts) still fires fetch normally β€” regression check + * on the existing path + */ +import { describe, test, expect } from 'vitest'; +import { Memeputer, keypairSigner } from '../src/index.js'; +import type { DryRunPostResult } from '../src/rooms.js'; +import { canonical } from '../src/internal/canonical.js'; +import { Keypair } from '@solana/web3.js'; + +function buildMpWithCaptor() { + const seen: Array<{ url: string; init?: RequestInit }> = []; + const captor: typeof fetch = async (url, init) => { + seen.push({ url: String(url), init }); + return new Response( + JSON.stringify({ + id: 'fake-message-id', + body: 'fake', + wallet: 'fake', + createdAt: new Date().toISOString(), + }), + { status: 201, headers: { 'Content-Type': 'application/json' } }, + ); + }; + const kp = Keypair.fromSeed(new Uint8Array(32).fill(7)); + const mp = new Memeputer({ + apiUrl: 'https://api.memeputer.com', + signer: keypairSigner(kp), + fetch: captor, + }); + return { mp, kp, seen }; +} + +const MINT = 'So11111111111111111111111111111111111111112'; // Wrapped SOL β€” stable test mint +const BODY = { body: 'gm' }; + +describe('mp.rooms.post({ dryRun: true })', () => { + test('returns DryRunPostResult with correct discriminator + shape', async () => { + const { mp } = buildMpWithCaptor(); + const result = await mp.rooms.post(MINT, BODY, { dryRun: true }); + // Narrowing works via the dryRun: true overload β€” but assert at runtime too. + expect(result.dryRun).toBe(true); + const r = result as DryRunPostResult; + expect(r.method).toBe('POST'); + expect(r.path).toBe(`/v1/rooms/${MINT}/messages`); + expect(r.body).toEqual(BODY); + expect(r.headers['X-Memeputer-Wallet']).toBeTypeOf('string'); + expect(r.headers['X-Memeputer-Signature']).toBeTypeOf('string'); + expect(r.headers['X-Memeputer-Timestamp']).toMatch(/^\d+$/); + expect(r.headers['X-Memeputer-Nonce']).toBeTypeOf('string'); + expect(r.headers['Content-Type']).toBe('application/json'); + expect(r.canonicalPayloadHex).toMatch(/^[0-9a-f]+$/i); + }); + + test('canonicalPayloadHex byte-equals ../src/internal/canonical.js canonical() β€” Phase 1 encoder anchor', async () => { + const { mp } = buildMpWithCaptor(); + const result = await mp.rooms.post(MINT, BODY, { dryRun: true }); + const r = result as DryRunPostResult; + const expected = canonical({ + method: 'POST', + path: `/v1/rooms/${MINT}/messages`, + body: BODY, + }); + const actual = Buffer.from(r.canonicalPayloadHex, 'hex'); + expect(actual.equals(Buffer.from(expected))).toBe(true); + }); + + test('dryRun: true does NOT fire fetch (no-network-spend contract)', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.post(MINT, BODY, { dryRun: true }); + expect(seen).toHaveLength(0); + }); + + test('standard post (no opts) still fires fetch β€” regression check', async () => { + const { mp, seen } = buildMpWithCaptor(); + const result = await mp.rooms.post(MINT, BODY); + expect(seen).toHaveLength(1); + expect(seen[0]?.url).toContain(`/v1/rooms/${MINT}/messages`); + // Non-dryRun path returns ApiMessage shape (not DryRunPostResult) + expect((result as { dryRun?: boolean }).dryRun).toBeUndefined(); + expect((result as { id?: string }).id).toBe('fake-message-id'); + }); +}); diff --git a/test/rooms-methods.test.ts b/test/rooms-methods.test.ts new file mode 100644 index 0000000..a1add45 --- /dev/null +++ b/test/rooms-methods.test.ts @@ -0,0 +1,189 @@ +import { describe, test, expect } from 'vitest'; +import { Keypair } from '@solana/web3.js'; +import { Memeputer, keypairSigner } from '../src/index.js'; + +/** + * RoomsNamespace method-dispatch tests (Plan 06-02 Task 2). + * + * Verifies the 9 non-WS methods route to the documented URL/method/body. + * Subscribe wiring is covered in subscribe.test.ts (mocks socket.io-client). + */ + +function buildMpWithCaptor(responseBody = '{"ok":true,"items":[]}') { + const seen: Array<{ url: string; method: string; body: unknown }> = []; + const fakeFetch: typeof fetch = async (url, init) => { + seen.push({ + url: url as string, + method: (init?.method as string) ?? 'GET', + body: init?.body ? JSON.parse(init.body as string) : null, + }); + return new Response(responseBody, { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + }; + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(60))), + apiUrl: 'http://localhost:3001', + fetch: fakeFetch, + }); + return { mp, seen }; +} + +describe('RoomsNamespace method dispatch', () => { + test('create β†’ POST /v1/rooms with full body', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.create({ + displayName: 'X', + imageUrl: 'https://media.memeputer.com/x.png', + accessType: 'both', + }); + expect(seen[0]!.method).toBe('POST'); + expect(seen[0]!.url).toBe('http://localhost:3001/v1/rooms'); + expect(seen[0]!.body).toEqual({ + displayName: 'X', + imageUrl: 'https://media.memeputer.com/x.png', + accessType: 'both', + }); + }); + + test('patch β†’ PATCH /v1/rooms/:mint', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.patch('MintX', { displayName: 'New' }); + expect(seen[0]!.method).toBe('PATCH'); + expect(seen[0]!.url).toBe('http://localhost:3001/v1/rooms/MintX'); + expect(seen[0]!.body).toEqual({ displayName: 'New' }); + }); + + test('list β†’ GET /v1/rooms?sort=mcap&limit=20&offset=0', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.list({ sort: 'mcap', limit: 20, offset: 0 }); + expect(seen[0]!.method).toBe('GET'); + expect(seen[0]!.url).toBe( + 'http://localhost:3001/v1/rooms?sort=mcap&limit=20&offset=0', + ); + }); + + test('list with no args β†’ GET /v1/rooms (no query string)', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.list(); + expect(seen[0]!.method).toBe('GET'); + expect(seen[0]!.url).toBe('http://localhost:3001/v1/rooms'); + }); + + test('get β†’ GET /v1/rooms/:mint', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.get('MintX'); + expect(seen[0]!.method).toBe('GET'); + expect(seen[0]!.url).toBe('http://localhost:3001/v1/rooms/MintX'); + }); + + test('post β†’ POST /v1/rooms/:mint/messages with body+parentMessageId', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.post('MintX', { body: 'gm', parentMessageId: '01ABCDE' }); + expect(seen[0]!.method).toBe('POST'); + expect(seen[0]!.url).toBe('http://localhost:3001/v1/rooms/MintX/messages'); + expect(seen[0]!.body).toEqual({ body: 'gm', parentMessageId: '01ABCDE' }); + }); + + test('messages β†’ GET /v1/rooms/:mint/messages?limit=50&before=abc123', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.messages('MintX', { limit: 50, before: 'abc123' }); + expect(seen[0]!.url).toContain('limit=50'); + expect(seen[0]!.url).toContain('before=abc123'); + }); + + test('members β†’ GET /v1/rooms/:mint/members?limit=20', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.members('MintX', { limit: 20 }); + expect(seen[0]!.url).toBe('http://localhost:3001/v1/rooms/MintX/members?limit=20'); + }); + + // CR-01 regression: response shape was previously typed as a flat + // `{ items, next_cursor }` but the wire ships `{ owner, members: { items, + // next_cursor } }`. Assert the wire shape so future drift fails CI. + test('members β†’ response carries `owner` + nested `members.items/next_cursor`', async () => { + const ownerWallet = '11111111111111111111111111111111'; + const memberWallet = '22222222222222222222222222222222'; + const responseBody = JSON.stringify({ + owner: { + wallet: ownerWallet, + display_name: 'Owner', + username: 'owner', + avatar_url: null, + bio_excerpt: '', + balance: '1000000', + eligible: true, + type: 'agent', + }, + members: { + items: [ + { + wallet: memberWallet, + display_name: 'Mem', + username: 'mem', + avatar_url: null, + bio_excerpt: '', + balance: '500', + eligible: true, + type: 'agent', + }, + ], + next_cursor: null, + }, + }); + const { mp } = buildMpWithCaptor(responseBody); + const page = await mp.rooms.members('MintX'); + expect(page.owner.wallet).toBe(ownerWallet); + expect(page.members.items).toHaveLength(1); + expect(page.members.items[0]!.wallet).toBe(memberWallet); + expect(page.members.next_cursor).toBeNull(); + }); + + test('search β†’ GET /v1/search?q=gm&type=messages&limit=10', async () => { + const { mp, seen } = buildMpWithCaptor(); + await mp.rooms.search({ q: 'gm', type: 'messages', limit: 10 }); + const url = seen[0]!.url; + expect(url).toContain('q=gm'); + expect(url).toContain('type=messages'); + expect(url).toContain('limit=10'); + }); + + // CR-02 regression: SearchResult was previously typed as `{ messages, + // rooms, agents }` but the wire ships `{ query, results: SearchHit[] }` + // with a `kind` discriminator. Assert the wire shape so future drift + // fails CI. + test('search β†’ response carries `query` + flat `results[]` with kind discriminator', async () => { + const responseBody = JSON.stringify({ + query: 'gm', + results: [ + { + kind: 'message', + id: '01M', + title: 'gm world', + subtitle: null, + room_mint: 'MintX', + agent_wallet: 'Wallet1', + rank: 0.8, + created_at: '2026-05-17T00:00:00.000Z', + }, + { + kind: 'room', + id: 'MintX', + title: 'Greeters', + subtitle: null, + room_mint: 'MintX', + agent_wallet: null, + rank: 0.5, + created_at: '2026-05-17T00:00:00.000Z', + }, + ], + }); + const { mp } = buildMpWithCaptor(responseBody); + const page = await mp.rooms.search({ q: 'gm' }); + expect(page.query).toBe('gm'); + expect(page.results).toHaveLength(2); + expect(page.results[0]!.kind).toBe('message'); + expect(page.results[1]!.kind).toBe('room'); + }); +}); diff --git a/test/rooms.claim-fees.test.ts b/test/rooms.claim-fees.test.ts new file mode 100644 index 0000000..6166065 --- /dev/null +++ b/test/rooms.claim-fees.test.ts @@ -0,0 +1,379 @@ +/** + * Plan 06.1-06 Task 2 β€” Vitest coverage for `mp.rooms.claimFees` + + * `mp.rooms.feeBalance`. + * + * Strategy: mock `@coral-xyz/anchor` so Program returns a fixture-driven + * shape ({ account.feeLedger.fetch, account.platformConfig.fetch, + * methods.claimCreatorReward(...).accounts(...).instruction }). Mock the + * Connection methods used by claimFees (getLatestBlockhash, sendTransaction, + * confirmTransaction) so no real RPC round-trip happens. + * + * Asserted invariants: + * 1. Happy path β€” full ix build + send β†’ returns ClaimFeesResult with + * bigint amounts AND correct fee math (1% bps deduction). + * 2. WRONG_SIGNER β€” fires BEFORE any sendTransaction call (guard 1 of 2 + * for the on-chain creator-wallet constraint). + * 3. LEDGER_NOT_INITIALIZED β€” Anchor fetch reject wraps as a 404 + * MemeputerApiError with the mint in details. + * 4. CLAIM_BELOW_MINIMUM β€” claimable < MIN_CLAIM_LAMPORTS (100_000n) + * throws BEFORE any sendTransaction call. + * 5. opts.receiver redirect β€” recipient arg + accounts.recipient both + * reflect the override pubkey. + * 6. feeBalance happy path β€” bigint coercion + lastSweptAt Date. + * 7. feeBalance with lastSweptAt = 0 β†’ null sentinel. + * 8. feeBalance LEDGER_NOT_INITIALIZED. + * 9. RPC_FAILED β€” sendTransaction reject wraps as MemeputerApiError. + * + * NOTE: This test mocks the @coral-xyz/anchor module BEFORE importing the + * SDK so vitest's hoist behaviour applies. We do NOT spin up a real + * Connection β€” see init-platform-config.ts (Plan 03 output) for the + * integration smoke against the live devnet program. + */ +import { describe, test, expect, vi, beforeEach } from 'vitest'; +import { + Keypair, + PublicKey, + type Connection, + type VersionedTransaction, +} from '@solana/web3.js'; + +// Module-scoped state for the Anchor mock. Each test resets these via +// the `setup()` helper so per-test fixtures don't bleed between cases. +type MockFeeLedger = { + bump: number; + mint: PublicKey; + creatorWallet: PublicKey; + accrued: { toString(): string }; + claimed: { toString(): string }; + lastSweptAt: { toString(): string }; + lastClaimAt: { toString(): string }; + reserved: number[]; +}; +type MockPlatformConfig = { + bump: number; + authority: PublicKey; + claimFeeBps: { toString(): string }; + platformFeeRecipient: PublicKey; + reserved: number[]; +}; + +interface MockState { + feeLedger: MockFeeLedger | Error; // Error β†’ fetch rejects + platformConfig: MockPlatformConfig; + capturedReceiverArg: PublicKey | null; // for the .receiver-redirect test + capturedAccountsArg: Record | null; +} + +const mockState: MockState = { + feeLedger: new Error('placeholder'), + platformConfig: { + bump: 255, + authority: PublicKey.default, + claimFeeBps: { toString: () => '100' }, // 1% + platformFeeRecipient: new PublicKey('11111111111111111111111111111112'), + reserved: [], + }, + capturedReceiverArg: null, + capturedAccountsArg: null, +}; + +vi.mock('@coral-xyz/anchor', () => { + class FakeProgram { + public readonly account: { + feeLedger: { fetch: (pda: PublicKey) => Promise }; + platformConfig: { fetch: (pda: PublicKey) => Promise }; + }; + public readonly methods: { + claimCreatorReward: (receiver: PublicKey) => { + accounts: (accts: Record) => { + instruction: () => Promise<{ programId: PublicKey; keys: []; data: Buffer }>; + }; + }; + }; + constructor() { + this.account = { + feeLedger: { + fetch: async () => { + if (mockState.feeLedger instanceof Error) throw mockState.feeLedger; + return mockState.feeLedger; + }, + }, + platformConfig: { + fetch: async () => mockState.platformConfig, + }, + }; + this.methods = { + claimCreatorReward: (receiver: PublicKey) => { + mockState.capturedReceiverArg = receiver; + return { + accounts: (accts: Record) => { + mockState.capturedAccountsArg = accts; + return { + instruction: async () => ({ + programId: new PublicKey('11111111111111111111111111111111'), + keys: [], + data: Buffer.alloc(0), + }), + }; + }, + }; + }, + }; + } + } + class FakeAnchorProvider { + constructor(_c: unknown, _w: unknown) { + // no-op; the test doesn't exercise provider RPC + } + } + return { + Program: FakeProgram, + AnchorProvider: FakeAnchorProvider, + BN: class FakeBN { + constructor(private v: number) {} + toString(): string { + return String(this.v); + } + }, + }; +}); + +// Imported AFTER vi.mock so the mocked Anchor is wired in. Vitest hoists +// vi.mock above imports automatically, but writing them after the call +// keeps the order obvious to a reader. +import { Memeputer, MemeputerApiError, keypairSigner } from '../src/index.js'; + +// Use a deterministic seed so the test signer pubkey is stable across runs. +const CREATOR_KP = Keypair.fromSeed(new Uint8Array(32).fill(7)); +const OTHER_KP = Keypair.fromSeed(new Uint8Array(32).fill(9)); +const TEST_MINT = new PublicKey('11111111111111111111111111111113'); +const OVERRIDE_RECEIVER = new PublicKey('11111111111111111111111111111114'); + +interface MockConnection { + getLatestBlockhash: ReturnType; + sendTransaction: ReturnType; + confirmTransaction: ReturnType; +} + +function buildMockConnection(opts?: { sendThrows?: Error }): MockConnection { + return { + getLatestBlockhash: vi.fn(async () => ({ + blockhash: '4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi', + lastValidBlockHeight: 1_000_000, + })), + sendTransaction: vi.fn(async () => { + if (opts?.sendThrows) throw opts.sendThrows; + return 'FakeSignature1111111111111111111111111111111111111111111111111111111'; + }), + confirmTransaction: vi.fn(async () => ({ value: { err: null } })), + }; +} + +function setup(opts?: { + creatorWallet?: PublicKey; + accrued?: bigint; + claimed?: bigint; + ledgerThrows?: boolean; + lastSweptAt?: bigint; + sendThrows?: Error; +}) { + mockState.capturedReceiverArg = null; + mockState.capturedAccountsArg = null; + if (opts?.ledgerThrows) { + mockState.feeLedger = new Error('Account does not exist'); + } else { + mockState.feeLedger = { + bump: 255, + mint: TEST_MINT, + creatorWallet: opts?.creatorWallet ?? CREATOR_KP.publicKey, + accrued: { toString: () => String(opts?.accrued ?? 1_000_000n) }, + claimed: { toString: () => String(opts?.claimed ?? 0n) }, + lastSweptAt: { toString: () => String(opts?.lastSweptAt ?? 1_747_400_000n) }, + lastClaimAt: { toString: () => '0' }, + reserved: [], + }; + } + const conn = buildMockConnection({ sendThrows: opts?.sendThrows }); + const mp = new Memeputer({ + apiUrl: 'http://localhost:3001', + signer: keypairSigner(CREATOR_KP), + // Cast: the SDK only touches the three methods we mock; full Connection + // shape is irrelevant. + connection: conn as unknown as Connection, + }); + return { mp, conn }; +} + +describe('mp.rooms.claimFees', () => { + beforeEach(() => { + mockState.capturedReceiverArg = null; + mockState.capturedAccountsArg = null; + }); + + test('1. happy path β€” returns ClaimFeesResult with bigint amounts + correct fee math', async () => { + // 1_000_000 lamports accrued; claim_fee_bps = 100 (1%); + // expected claimFee = 10_000; netClaimed = 990_000. + const { mp, conn } = setup({ accrued: 1_000_000n, claimed: 0n }); + const result = await mp.rooms.claimFees(TEST_MINT); + expect(result.grossClaimed).toBe(1_000_000n); + expect(result.claimFee).toBe(10_000n); + expect(result.netClaimed).toBe(990_000n); + expect(typeof result.txSignature).toBe('string'); + expect(result.txSignature).toMatch(/^FakeSignature/); + expect(conn.sendTransaction).toHaveBeenCalledTimes(1); + expect(conn.confirmTransaction).toHaveBeenCalledTimes(1); + }); + + test('2. WRONG_SIGNER β€” fires BEFORE sendTransaction when signer != ledger.creatorWallet', async () => { + const { mp, conn } = setup({ + creatorWallet: OTHER_KP.publicKey, // ledger says someone else owns the rewards + accrued: 1_000_000n, + }); + await expect(mp.rooms.claimFees(TEST_MINT)).rejects.toMatchObject({ + code: 'WRONG_SIGNER', + status: 403, + }); + // The off-chain guard MUST fire before any tx is sent. + expect(conn.sendTransaction).not.toHaveBeenCalled(); + }); + + test('3. LEDGER_NOT_INITIALIZED β€” Anchor fetch reject wraps as 404 MemeputerApiError', async () => { + const { mp, conn } = setup({ ledgerThrows: true }); + try { + await mp.rooms.claimFees(TEST_MINT); + throw new Error('should have thrown'); + } catch (err) { + expect(err).toBeInstanceOf(MemeputerApiError); + expect((err as MemeputerApiError).code).toBe('LEDGER_NOT_INITIALIZED'); + expect((err as MemeputerApiError).status).toBe(404); + expect((err as MemeputerApiError).details).toMatchObject({ + mint: TEST_MINT.toBase58(), + }); + } + expect(conn.sendTransaction).not.toHaveBeenCalled(); + }); + + test('4. CLAIM_BELOW_MINIMUM β€” claimable < MIN_CLAIM_LAMPORTS throws before send', async () => { + // 50_000 < MIN_CLAIM_LAMPORTS (100_000) β†’ 400 BELOW_MINIMUM. + const { mp, conn } = setup({ accrued: 50_000n, claimed: 0n }); + await expect(mp.rooms.claimFees(TEST_MINT)).rejects.toMatchObject({ + code: 'CLAIM_BELOW_MINIMUM', + status: 400, + }); + expect(conn.sendTransaction).not.toHaveBeenCalled(); + }); + + test('5. opts.receiver β€” recipient arg + accounts.recipient reflect the override', async () => { + const { mp } = setup({ accrued: 1_000_000n }); + await mp.rooms.claimFees(TEST_MINT, { receiver: OVERRIDE_RECEIVER }); + expect(mockState.capturedReceiverArg?.toBase58()).toBe( + OVERRIDE_RECEIVER.toBase58(), + ); + expect(mockState.capturedAccountsArg?.recipient?.toBase58()).toBe( + OVERRIDE_RECEIVER.toBase58(), + ); + }); + + test('6. RPC_FAILED β€” sendTransaction reject wraps as MemeputerApiError', async () => { + const { mp } = setup({ + accrued: 1_000_000n, + sendThrows: new Error('blockhash not found'), + }); + await expect(mp.rooms.claimFees(TEST_MINT)).rejects.toMatchObject({ + code: 'RPC_FAILED', + status: 502, + }); + }); + + test('7. accepts string mint β€” normalized to PublicKey internally', async () => { + // Regression guard: the public method signature takes string | PublicKey. + // The stringβ†’PublicKey coercion must not throw for valid base58. + const { mp } = setup({ accrued: 1_000_000n }); + const result = await mp.rooms.claimFees(TEST_MINT.toBase58()); + expect(result.grossClaimed).toBe(1_000_000n); + }); +}); + +describe('mp.rooms.feeBalance', () => { + test('8. happy path β€” returns FeeBalanceResult with bigint coercion + lastSweptAt Date', async () => { + const { mp } = setup({ + accrued: 5_000_000n, + claimed: 2_000_000n, + lastSweptAt: 1_747_400_000n, + }); + const balance = await mp.rooms.feeBalance(TEST_MINT); + expect(balance.accrued).toBe(5_000_000n); + expect(balance.claimed).toBe(2_000_000n); + expect(balance.claimable).toBe(3_000_000n); + expect(balance.lastSweptAt).toBeInstanceOf(Date); + // 1_747_400_000 seconds = 2025-05-16T13:53:20Z (sanity check, not exact) + expect(balance.lastSweptAt!.getUTCFullYear()).toBeGreaterThanOrEqual(2025); + }); + + test('9. lastSweptAt=0 returns null sentinel (never-swept ledger)', async () => { + const { mp } = setup({ accrued: 1_000_000n, lastSweptAt: 0n }); + const balance = await mp.rooms.feeBalance(TEST_MINT); + expect(balance.lastSweptAt).toBeNull(); + }); + + test('10. LEDGER_NOT_INITIALIZED β€” fetch reject wraps as 404 error', async () => { + const { mp } = setup({ ledgerThrows: true }); + await expect(mp.rooms.feeBalance(TEST_MINT)).rejects.toMatchObject({ + code: 'LEDGER_NOT_INITIALIZED', + status: 404, + }); + }); +}); + +describe('mp.rooms.* setup gates', () => { + test('11. claimFees throws RPC_FAILED if no connection in ClientOpts', async () => { + const mp = new Memeputer({ + apiUrl: 'http://localhost:3001', + signer: keypairSigner(CREATOR_KP), + // No `connection` field. + }); + await expect(mp.rooms.claimFees(TEST_MINT)).rejects.toMatchObject({ + code: 'RPC_FAILED', + status: 500, + }); + }); + + test('12. feeBalance throws RPC_FAILED if no connection in ClientOpts', async () => { + const mp = new Memeputer({ + apiUrl: 'http://localhost:3001', + signer: keypairSigner(CREATOR_KP), + }); + await expect(mp.rooms.feeBalance(TEST_MINT)).rejects.toMatchObject({ + code: 'RPC_FAILED', + status: 500, + }); + }); +}); + +// Ensures keypairSigner picks up the new signTransaction method shipped in +// Plan 06.1-06 Task 2. Pure-unit; doesn't need the Anchor mock. +describe('keypairSigner.signTransaction', () => { + test('13. signs VersionedTransaction in place and returns it', async () => { + const signer = keypairSigner(CREATOR_KP); + expect(typeof signer.signTransaction).toBe('function'); + // Construct a minimal VersionedTransaction with a fake message. + const { TransactionMessage, VersionedTransaction, SystemProgram } = await import( + '@solana/web3.js' + ); + const msg = new TransactionMessage({ + payerKey: CREATOR_KP.publicKey, + recentBlockhash: '4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi', + instructions: [ + SystemProgram.transfer({ + fromPubkey: CREATOR_KP.publicKey, + toPubkey: OTHER_KP.publicKey, + lamports: 1, + }), + ], + }).compileToV0Message(); + const tx = new VersionedTransaction(msg); + const signed = await signer.signTransaction!(tx as unknown as VersionedTransaction); + // Signature slot 0 should be populated (non-zero bytes) + expect(signed.signatures[0]!.some((b) => b !== 0)).toBe(true); + }); +}); diff --git a/test/signer-keypair.test.ts b/test/signer-keypair.test.ts new file mode 100644 index 0000000..5bdec58 --- /dev/null +++ b/test/signer-keypair.test.ts @@ -0,0 +1,20 @@ +import { describe, test, expect } from 'vitest'; +import { Keypair } from '@solana/web3.js'; +import nacl from 'tweetnacl'; +import { keypairSigner } from '../src/index.js'; + +describe('keypairSigner adapter', () => { + test('signMessage returns 64-byte raw Ed25519 sig verifiable by nacl.sign.detached.verify', async () => { + const kp = Keypair.fromSeed(new Uint8Array(32).fill(42)); + const signer = keypairSigner(kp); + const msg = new TextEncoder().encode('hello memeputer'); + const sig = await signer.signMessage(msg); + expect(sig.length).toBe(64); + expect(nacl.sign.detached.verify(msg, sig, kp.publicKey.toBytes())).toBe(true); + }); + + test('publicKey identity preserved', () => { + const kp = Keypair.generate(); + expect(keypairSigner(kp).publicKey.toBase58()).toBe(kp.publicKey.toBase58()); + }); +}); diff --git a/test/subscribe.test.ts b/test/subscribe.test.ts new file mode 100644 index 0000000..938343d --- /dev/null +++ b/test/subscribe.test.ts @@ -0,0 +1,117 @@ +import { describe, test, expect, vi, beforeEach } from 'vitest'; + +/** + * mp.rooms.subscribe wiring tests (Plan 06-02 Task 2). + * + * Verifies the Socket.IO wiring via vi.mock of `socket.io-client`: + * - opens io(wsBase, { path: '/ws', transports: ['websocket'], reconnection: true }) + * - emits 'subscribe' with the mint on the 'connect' callback + * - invokes the user callback when 'message' fires + * - the returned unsub function calls socket.disconnect() + * + * The mock returns a socket object whose `on` recorder lets the test + * synthesize the connect/message events without a real Socket.IO server. + */ + +const emitSpy = vi.fn(); +const onMap = new Map void>(); +const onSpy = vi.fn((evt: string, cb: (arg: unknown) => void) => { + onMap.set(evt, cb); +}); +const disconnectSpy = vi.fn(); +const ioMock = vi.fn(() => ({ on: onSpy, emit: emitSpy, disconnect: disconnectSpy })); + +vi.mock('socket.io-client', () => ({ io: ioMock, default: { io: ioMock } })); + +beforeEach(() => { + emitSpy.mockClear(); + onSpy.mockClear(); + disconnectSpy.mockClear(); + onMap.clear(); + ioMock.mockClear(); +}); + +describe('mp.rooms.subscribe wiring', () => { + test('opens socket.io-client with path /ws, transports: [websocket]; emits subscribe on connect; routes message events to cb; unsub disconnects', async () => { + // Dynamic import keeps vi.mock active at the right hoist point. + const { Memeputer, keypairSigner } = await import('../src/index.js'); + const { Keypair } = await import('@solana/web3.js'); + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(80))), + apiUrl: 'http://localhost:3001', + }); + const received: unknown[] = []; + const unsub = mp.rooms.subscribe('MintX', (e) => received.push(e)); + + // Wait for the dynamic import inside subscribe() to resolve. + await new Promise((r) => setTimeout(r, 25)); + + expect(ioMock).toHaveBeenCalledTimes(1); + // First argument: wsBase derived by replacing httpβ†’ws (T-06-01-01 schema). + expect(ioMock.mock.calls[0]![0]).toBe('ws://localhost:3001'); + expect(ioMock.mock.calls[0]![1]).toMatchObject({ + path: '/ws', + transports: ['websocket'], + reconnection: true, + }); + + // Simulate 'connect' β†’ SDK should emit subscribe. + const connectCb = onMap.get('connect'); + expect(connectCb).toBeDefined(); + connectCb!(undefined); + expect(emitSpy).toHaveBeenCalledWith('subscribe', { mint: 'MintX' }); + + // Simulate 'message' β†’ cb invoked. + const evt = { + room_mint: 'MintX', + message_id: '01ABC', + agent_wallet: 'W', + body: 'gm', + parent_message_id: null, + created_at: '2026-05-17T00:00:00Z', + }; + const msgCb = onMap.get('message'); + expect(msgCb).toBeDefined(); + msgCb!(evt); + expect(received[0]).toEqual(evt); + + // Unsub disconnects. + unsub(); + expect(disconnectSpy).toHaveBeenCalledTimes(1); + }); + + // WR-10: subscribe_rejected from the server (e.g. ROOM_AT_CAPACITY) must + // surface as a MemeputerApiError rather than be silently swallowed. + test('subscribe_rejected event surfaces a MemeputerApiError via queueMicrotask', async () => { + const { Memeputer, keypairSigner, MemeputerApiError } = await import( + '../src/index.js' + ); + const { Keypair } = await import('@solana/web3.js'); + const mp = new Memeputer({ + signer: keypairSigner(Keypair.fromSeed(new Uint8Array(32).fill(81))), + apiUrl: 'http://localhost:3001', + }); + mp.rooms.subscribe('MintX', () => {}); + await new Promise((r) => setTimeout(r, 25)); + + const rejectedCb = onMap.get('subscribe_rejected'); + expect(rejectedCb).toBeDefined(); + + // Capture the queueMicrotask throw without crashing the test. + let captured: unknown; + const originalHandler = process.listeners('uncaughtException').slice(); + process.removeAllListeners('uncaughtException'); + process.once('uncaughtException', (err) => { + captured = err; + }); + rejectedCb!({ code: 'ROOM_AT_CAPACITY', message: 'room is full', status: 503 }); + await new Promise((r) => setTimeout(r, 5)); + // Restore prior handlers. + for (const h of originalHandler) process.on('uncaughtException', h); + + expect(captured).toBeInstanceOf(MemeputerApiError); + expect((captured as InstanceType).code).toBe( + 'ROOM_AT_CAPACITY', + ); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 6098260..e61c209 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,24 +1,19 @@ { "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": ["ES2020"], + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022", "DOM"], "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "moduleResolution": "node", "declaration": true, "declarationMap": true, "sourceMap": true, - "composite": true, - "incremental": true, - "outDir": "./dist" + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src", + "skipLibCheck": true }, - "references": [ - { "path": "./packages/cli" } - ], - "exclude": ["node_modules", "dist", "**/dist"] + "include": ["src"] } - diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..3e07e5f --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: { + index: 'src/index.ts', + cli: 'src/cli.mts', + }, + format: ['esm', 'cjs'], + dts: true, + splitting: false, + sourcemap: true, + clean: true, + platform: 'node', + outExtension({ format }) { + // tsup v8 with package.json `"type": "module"` emits: + // ESM β†’ .mjs + .d.ts | CJS β†’ .cjs + .d.cts + // (dts extension is decided by tsup's defaultOutExtension and ignores + // a user-supplied dts override, so we don't try to set it here.) + return format === 'esm' ? { js: '.mjs' } : { js: '.cjs' }; + }, +}); diff --git a/packages/cli/vitest.config.ts b/vitest.config.ts similarity index 66% rename from packages/cli/vitest.config.ts rename to vitest.config.ts index 9fe1d91..5fc8101 100644 --- a/packages/cli/vitest.config.ts +++ b/vitest.config.ts @@ -2,8 +2,8 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - globals: true, environment: 'node', + include: ['test/**/*.test.ts'], + testTimeout: 10_000, }, }); -