diff --git a/.cursor/mcp.json b/.cursor/mcp.json deleted file mode 100644 index 42a6a03..0000000 --- a/.cursor/mcp.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "mcpServers": { - "evm-mcp-server": { - "command": "npx", - "args": [ - "-y", - "@mcpdotdirect/evm-mcp-server" - ] - }, - "evm-mcp-http": { - "command": "npx", - "args": [ - "-y", - "@mcpdotdirect/evm-mcp-server", - "--http" - ] - } - } -} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 62ce640..2662ab7 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ build/ # Finder (MacOS) folder config .DS_Store + +.env \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c55757b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2748 @@ +{ + "name": "@mcpdotdirect/evm-mcp-server", + "version": "1.2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@mcpdotdirect/evm-mcp-server", + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.7.0", + "@supabase/ssr": "latest", + "@supabase/supabase-js": "latest", + "cors": "^2.8.5", + "ethers": "^6.14.3", + "express": "^4.21.2", + "viem": "^2.23.9", + "zod": "^3.24.2" + }, + "bin": { + "evm-mcp-server": "bin/cli.js" + }, + "devDependencies": { + "@types/bun": "latest", + "@types/cors": "^2.8.17", + "@types/express": "^5.0.0", + "conventional-changelog-cli": "^5.0.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "typescript": "^5.8.2" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz", + "integrity": "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==", + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@conventional-changelog/git-client": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-1.0.1.tgz", + "integrity": "sha512-PJEqBwAleffCMETaVm/fUgHldzBE35JFk3/9LL6NUA5EXa3qednu+UT6M7E5iBu3zIQZCULYIiZ90fBYHt6xUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/semver": "^7.5.5", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0" + }, + "peerDependenciesMeta": { + "conventional-commits-filter": { + "optional": true + }, + "conventional-commits-parser": { + "optional": true + } + } + }, + "node_modules/@hutson/parse-repository-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-5.0.0.tgz", + "integrity": "sha512-e5+YUKENATs1JgYHMzTr2MW/NDcXGfYFAuOQU8gJgF/kEh4EqKgfGrfLI67bMD4tbhZVlkigz/9YYwWcbOFthg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.1.tgz", + "integrity": "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@supabase/auth-js": { + "version": "2.70.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.70.0.tgz", + "integrity": "sha512-BaAK/tOAZFJtzF1sE3gJ2FwTjLf4ky3PSvcvLGEgEmO4BSBkwWKu8l67rLLIBZPDnCyV7Owk2uPyKHa0kj5QGg==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", + "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz", + "integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.11.10", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.10.tgz", + "integrity": "sha512-SJKVa7EejnuyfImrbzx+HaD9i6T784khuw1zP+MBD7BmJYChegGxYigPzkKX8CK8nGuDntmeSD3fvriaH0EGZA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.13", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "ws": "^8.18.2" + } + }, + "node_modules/@supabase/ssr": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.6.1.tgz", + "integrity": "sha512-QtQgEMvaDzr77Mk3vZ3jWg2/y+D8tExYF7vcJT+wQ8ysuvOeGGjYbZlvj5bHYsj/SpC0bihcisnwPrM4Gp5G4g==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1" + }, + "peerDependencies": { + "@supabase/supabase-js": "^2.43.4" + } + }, + "node_modules/@supabase/ssr/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz", + "integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.50.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.50.0.tgz", + "integrity": "sha512-M1Gd5tPaaghYZ9OjeO1iORRqbTWFEz/cF3pPubRnMPzA+A8SiUsXXWDP+DWsASZcjEcVEcVQIAF38i5wrijYOg==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.70.0", + "@supabase/functions-js": "2.4.4", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.19.4", + "@supabase/realtime-js": "2.11.10", + "@supabase/storage-js": "2.7.1" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bun": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.2.15.tgz", + "integrity": "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-types": "1.2.15" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.18", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.18.tgz", + "integrity": "sha512-nX3d0sxJW41CqQvfOzVG1NCTXfFDrDWIghCZncpHeWlVFd81zxB/DLhg7avFg6eHLCRX7ckBmoIIcqa++upvJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.2.tgz", + "integrity": "sha512-BtjL3ZwbCQriyb0DGw+Rt12qAXPiBTPs815lsUvtt1Grk0vLRMZNMUZ741d5rjk+UQOxfDiBZ3dxpX00vSkK3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", + "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bun-types": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.2.15.tgz", + "integrity": "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/conventional-changelog": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-6.0.0.tgz", + "integrity": "sha512-tuUH8H/19VjtD9Ig7l6TQRh+Z0Yt0NZ6w/cCkkyzUbGQTnUEmKfGtkC9gGfVgCfOL1Rzno5NgNF4KY8vR+Jo3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-atom": "^5.0.0", + "conventional-changelog-codemirror": "^5.0.0", + "conventional-changelog-conventionalcommits": "^8.0.0", + "conventional-changelog-core": "^8.0.0", + "conventional-changelog-ember": "^5.0.0", + "conventional-changelog-eslint": "^6.0.0", + "conventional-changelog-express": "^5.0.0", + "conventional-changelog-jquery": "^6.0.0", + "conventional-changelog-jshint": "^5.0.0", + "conventional-changelog-preset-loader": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", + "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-atom": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-5.0.0.tgz", + "integrity": "sha512-WfzCaAvSCFPkznnLgLnfacRAzjgqjLUjvf3MftfsJzQdDICqkOOpcMtdJF3wTerxSpv2IAAjX8doM3Vozqle3g==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-cli": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-5.0.0.tgz", + "integrity": "sha512-9Y8fucJe18/6ef6ZlyIlT2YQUbczvoQZZuYmDLaGvcSBP+M6h+LAvf7ON7waRxKJemcCII8Yqu5/8HEfskTxJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog": "^6.0.0", + "meow": "^13.0.0", + "tempfile": "^5.0.0" + }, + "bin": { + "conventional-changelog": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-codemirror": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-5.0.0.tgz", + "integrity": "sha512-8gsBDI5Y3vrKUCxN6Ue8xr6occZ5nsDEc4C7jO/EovFGozx8uttCAyfhRrvoUAWi2WMm3OmYs+0mPJU7kQdYWQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-8.0.0.tgz", + "integrity": "sha512-eOvlTO6OcySPyyyk8pKz2dP4jjElYunj9hn9/s0OB+gapTO8zwS9UQWrZ1pmF2hFs3vw1xhonOLGcGjy/zgsuA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-core": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-8.0.0.tgz", + "integrity": "sha512-EATUx5y9xewpEe10UEGNpbSHRC6cVZgO+hXQjofMqpy+gFIrcGvH3Fl6yk2VFKh7m+ffenup2N7SZJYpyD9evw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hutson/parse-repository-url": "^5.0.0", + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-parser": "^6.0.0", + "git-raw-commits": "^5.0.0", + "git-semver-tags": "^8.0.0", + "hosted-git-info": "^7.0.0", + "normalize-package-data": "^6.0.0", + "read-package-up": "^11.0.0", + "read-pkg": "^9.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-ember": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-5.0.0.tgz", + "integrity": "sha512-RPflVfm5s4cSO33GH/Ey26oxhiC67akcxSKL8CLRT3kQX2W3dbE19sSOM56iFqUJYEwv9mD9r6k79weWe1urfg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-eslint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-6.0.0.tgz", + "integrity": "sha512-eiUyULWjzq+ybPjXwU6NNRflApDWlPEQEHvI8UAItYW/h22RKkMnOAtfCZxMmrcMO1OKUWtcf2MxKYMWe9zJuw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-5.0.0.tgz", + "integrity": "sha512-D8Q6WctPkQpvr2HNCCmwU5GkX22BVHM0r4EW8vN0230TSyS/d6VQJDAxGb84lbg0dFjpO22MwmsikKL++Oo/oQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-jquery": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-6.0.0.tgz", + "integrity": "sha512-2kxmVakyehgyrho2ZHBi90v4AHswkGzHuTaoH40bmeNqUt20yEkDOSpw8HlPBfvEQBwGtbE+5HpRwzj6ac2UfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-jshint": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-5.0.0.tgz", + "integrity": "sha512-gGNphSb/opc76n2eWaO6ma4/Wqu3tpa2w7i9WYqI6Cs2fncDSI2/ihOfMvXveeTTeld0oFvwMVNV+IYQIk3F3g==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-preset-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-5.0.0.tgz", + "integrity": "sha512-SetDSntXLk8Jh1NOAl1Gu5uLiCNSYenB5tm0YVeZKePRIgDW9lQImromTwLa3c/Gae298tsgOM+/CYT9XAl0NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.1.0.tgz", + "integrity": "sha512-dpC440QnORNCO81XYuRRFOLCsjKj4W7tMkUIn3lR6F/FAaJcWLi7iCj6IcEvSQY2zw6VUgwUKd5DEHKEWrpmEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^5.0.0", + "handlebars": "^4.7.7", + "meow": "^13.0.0", + "semver": "^7.5.2" + }, + "bin": { + "conventional-changelog-writer": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.1.0.tgz", + "integrity": "sha512-5nxDo7TwKB5InYBl4ZC//1g9GRwB/F3TXOGR9hgUjMGfvSP4Vu5NkpNro2+1+TIEy1vwxApl5ircECr2ri5JIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ethers": { + "version": "6.14.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.14.3.tgz", + "integrity": "sha512-qq7ft/oCJohoTcsNPFaXSQUm457MA5iWqkf1Mb11ujONdg7jBI6sAOrHaTi3j0CBqIGFSCeR/RMc+qwRRub7IA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT" + }, + "node_modules/ethers/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/ethers/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.2.tgz", + "integrity": "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", + "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "^4.11 || 5 || ^5.0.0-beta.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/git-raw-commits": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-5.0.0.tgz", + "integrity": "sha512-I2ZXrXeOc0KrCvC7swqtIFXFN+rbjnC7b2T943tvemIOVNl+XP8YnA9UVwqFhzzLClnSA60KR/qEjLpXzs73Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@conventional-changelog/git-client": "^1.0.0", + "meow": "^13.0.0" + }, + "bin": { + "git-raw-commits": "src/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/git-semver-tags": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-8.0.0.tgz", + "integrity": "sha512-N7YRIklvPH3wYWAR2vysaqGLPRcpwQ0GKdlqTiVN5w1UmCdaeY3K8s6DMKRCh54DDdzyt/OAB6C8jgVtb7Y2Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@conventional-changelog/git-client": "^1.0.0", + "meow": "^13.0.0" + }, + "bin": { + "git-semver-tags": "src/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/index-to-position": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", + "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/ox": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.7.1.tgz", + "integrity": "sha512-+k9fY9PRNuAMHRFIUbiK9Nt5seYHHzSQs9Bj+iMETcGtlpS7SmBzcGSVUQO3+nqGLEiNK4598pHNFlVRaZbRsg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/router/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempfile": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-5.0.0.tgz", + "integrity": "sha512-bX655WZI/F7EoTDw9JvQURqAXiPHi8o8+yFxPF2lWYyz1aHnmMRuXWqL6YB6GmeO0o4DIYWHLgGNi/X64T+X4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "temp-dir": "^3.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/viem": { + "version": "2.30.6", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.30.6.tgz", + "integrity": "sha512-N3vGy3pZ+EVgQRuWqQhZPFXxQE8qMRrBd3uM+KLc1Ub2w6+vkyr7umeWQCM4c+wlsCdByUgh2630MDMLquMtpg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.0.8", + "isows": "1.0.7", + "ox": "0.7.1", + "ws": "8.18.2" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/zod": { + "version": "3.25.48", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.48.tgz", + "integrity": "sha512-0X1mz8FtgEIvaxGjdIImYpZEaZMrund9pGXm3M6vM7Reba0e2eI71KPjSCGXBfwKDPwPoywf6waUKc3/tFvX2Q==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.5", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", + "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/package.json b/package.json index ab33609..4e81d68 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,9 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.7.0", + "@supabase/ssr": "latest", + "@supabase/supabase-js": "latest", + "ethers": "^6.14.3", "cors": "^2.8.5", "express": "^4.21.2", "viem": "^2.23.9", diff --git a/src/core/chains.ts b/src/core/chains.ts index c5c5dda..2861259 100644 --- a/src/core/chains.ts +++ b/src/core/chains.ts @@ -282,6 +282,79 @@ export const rpcUrlMap: Record = { 314159: 'https://api.calibration.node.glif.io/rpc/v1', }; +export const scanApiKeyMap: Record = { + // Mainnets + 1: process.env.ETHERSCAN_API_KEY || null, + 11155111: process.env.ETHERSCAN_API_KEY || null, + 8453: process.env.BASESCAN_API_KEY || null, + 84532: process.env.BASESCAN_API_KEY || null, + 42161: process.env.ARBISCAN_API_KEY || null, + 42170: process.env.ARBISCAN_API_KEY || null, +}; + +// Map chain IDs to endpoint URLs +export const endpointMap: Record = { + // Mainnets + 1: 'https://api.etherscan.io/api', // Ethereum Mainnet + 10: 'https://api-optimistic.etherscan.io/api', // Optimism Mainnet + 42161: 'https://api.arbiscan.io/api', // Arbitrum One + 42170: 'https://api-nova.arbiscan.io/api', // Arbitrum Nova + 8453: 'https://api.basescan.org/api', // Base Mainnet + 137: 'https://api.polygonscan.com/api', // Polygon Mainnet + 1101: 'https://api-zkevm.polygonscan.com/api', // Polygon zkEVM + // Los siguientes endpoints no están disponibles públicamente o no tienen un explorador con API conocida: + 43114: '', // Avalanche C-Chain + 56: '', // BNB Smart Chain + 324: '', // zkSync Era + 59144: '', // Linea Mainnet + 42220: '', // Celo Mainnet + 100: '', // Gnosis Chain + 250: '', // Fantom Opera + 314: '', // Filecoin Mainnet + 1284: '', // Moonbeam + 1285: '', // Moonriver + 25: '', // Cronos Mainnet + 534352: '', // Scroll Mainnet + 5000: '', // Mantle Mainnet + 169: '', // Manta Pacific Mainnet + 81457: '', // Blast Mainnet + 252: '', // Fraxtal Mainnet + 994873017: '', // Redstone Mainnet + 34443: '', // Mode Mainnet + 1088: '', // Metis Mainnet + 255: '', // Kroma Mainnet + 7777777: '', // Zora Mainnet + 1313161554: '', // Aurora Mainnet + 7700: '', // Canto Mainnet + 747: '', // ShimmerEVM Mainnet + + // Testnets + 11155111: 'https://api-sepolia.etherscan.io/api', // Ethereum Sepolia + 11155420: 'https://api-sepolia-optimistic.etherscan.io/api', // Optimism Sepolia + 421614: 'https://api-sepolia.arbiscan.io/api', // Arbitrum Sepolia + 84532: 'https://api-sepolia.basescan.org/api', // Base Sepolia + 80002: 'https://api-testnet.polygonscan.com/api', // Polygon Mumbai + 43113: '', // Avalanche Fuji Testnet + 97: '', // BNB Smart Chain Testnet + 300: '', // zkSync Testnet + 59141: '', // Linea Testnet + 534351: '', // Scroll Sepolia Testnet + 5003: '', // Mantle Testnet + 3441006: '', // Mode Testnet + 1952959480: '', // Redstone Testnet + 168587773: '', // Blast Sepolia Testnet + 2522: '', // Fraxtal Sepolia Testnet + 919: '', // Manta Pacific Testnet + 59902: '', // Zora Sepolia Testnet + 2358: '', // Kroma Sepolia Testnet + 999999999: '', // Zora Testnet + 44787: '', // Celo Alfajores Testnet + 5: 'https://api-goerli.etherscan.io/api', // Ethereum Goerli (obsoleto) + 17000: 'https://api-holesky.etherscan.io/api', // Ethereum Holesky + 545: '', // ShimmerEVM Testnet + 314159: '', // Filecoin Calibration Testnet +}; + /** * Resolves a chain identifier (number or string) to a chain ID * @param chainIdentifier Chain ID (number) or network name (string) diff --git a/src/core/common_tools.ts b/src/core/common_tools.ts new file mode 100644 index 0000000..145fabc --- /dev/null +++ b/src/core/common_tools.ts @@ -0,0 +1,1196 @@ +// import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +// import { z } from "zod"; +// import { getSupportedNetworks, getRpcUrl } from "./chains.js"; +// import * as services from "./services/index.js"; +// import { type Address, type Hex, type Hash } from 'viem'; +// import { normalize } from 'viem/ens'; + +// /** +// * Register all EVM-related tools with the MCP server +// * +// * All tools that accept Ethereum addresses also support ENS names (e.g., 'vitalik.eth'). +// * ENS names are automatically resolved to addresses using the Ethereum Name Service. +// * +// * @param server The MCP server instance +// */ +// export function registerStaticTools(server: McpServer) { +// // NETWORK INFORMATION TOOLS + +// // Get chain information +// server.tool( +// "get_chain_info", +// "Get information about an EVM network", +// { +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") +// }, +// async ({ network = "ethereum" }) => { +// try { +// const chainId = await services.getChainId(network); +// const blockNumber = await services.getBlockNumber(network); +// const rpcUrl = getRpcUrl(network); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// network, +// chainId, +// blockNumber: blockNumber.toString(), +// rpcUrl +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching chain info: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // ENS LOOKUP TOOL + +// // Resolve ENS name to address +// server.tool( +// "resolve_ens", +// "Resolve an ENS name to an Ethereum address", +// { +// ensName: z.string().describe("ENS name to resolve (e.g., 'vitalik.eth')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. ENS resolution works best on Ethereum mainnet. Defaults to Ethereum mainnet.") +// }, +// async ({ ensName, network = "ethereum" }) => { +// try { +// // Validate that the input is an ENS name +// if (!ensName.includes('.')) { +// return { +// content: [{ +// type: "text", +// text: `Error: Input "${ensName}" is not a valid ENS name. ENS names must contain a dot (e.g., 'name.eth').` +// }], +// isError: true +// }; +// } + +// // Normalize the ENS name +// const normalizedEns = normalize(ensName); + +// // Resolve the ENS name to an address +// const address = await services.resolveAddress(ensName, network); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// ensName: ensName, +// normalizedName: normalizedEns, +// resolvedAddress: address, +// network +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error resolving ENS name: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Get supported networks +// server.tool( +// "get_supported_networks", +// "Get a list of supported EVM networks", +// {}, +// async () => { +// try { +// const networks = getSupportedNetworks(); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// supportedNetworks: networks +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching supported networks: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // BLOCK TOOLS + +// // Get block by number +// server.tool( +// "get_block_by_number", +// "Get a block by its block number", +// { +// blockNumber: z.number().describe("The block number to fetch"), +// network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ blockNumber, network = "ethereum" }) => { +// try { +// const block = await services.getBlockByNumber(blockNumber, network); + +// return { +// content: [{ +// type: "text", +// text: services.helpers.formatJson(block) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching block ${blockNumber}: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Get latest block +// server.tool( +// "get_latest_block", +// "Get the latest block from the EVM", +// { +// network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ network = "ethereum" }) => { +// try { +// const block = await services.getLatestBlock(network); + +// return { +// content: [{ +// type: "text", +// text: services.helpers.formatJson(block) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching latest block: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // BALANCE TOOLS + +// // Get ETH balance +// server.tool( +// "get_balance", +// "Get the native token balance (ETH, MATIC, etc.) for an address", +// { +// address: z.string().describe("The wallet address or ENS name (e.g., '0x1234...' or 'vitalik.eth') to check the balance for"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") +// }, +// async ({ address, network = "ethereum" }) => { +// try { +// const balance = await services.getETHBalance(address, network); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// address, +// network, +// wei: balance.wei.toString(), +// ether: balance.ether +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching balance: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Get ERC20 balance +// server.tool( +// "get_erc20_balance", +// "Get the ERC20 token balance of an Ethereum address", +// { +// address: z.string().describe("The Ethereum address to check"), +// tokenAddress: z.string().describe("The ERC20 token contract address"), +// network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ address, tokenAddress, network = "ethereum" }) => { +// try { +// const balance = await services.getERC20Balance( +// tokenAddress as Address, +// address as Address, +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// address, +// tokenAddress, +// network, +// balance: { +// raw: balance.raw.toString(), +// formatted: balance.formatted, +// decimals: balance.token.decimals +// } +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching ERC20 balance for ${address}: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Get ERC20 token balance +// server.tool( +// "get_token_balance", +// "Get the balance of an ERC20 token for an address", +// { +// tokenAddress: z.string().describe("The contract address or ENS name of the ERC20 token (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC or 'uniswap.eth')"), +// ownerAddress: z.string().describe("The wallet address or ENS name to check the balance for (e.g., '0x1234...' or 'vitalik.eth')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") +// }, +// async ({ tokenAddress, ownerAddress, network = "ethereum" }) => { +// try { +// const balance = await services.getERC20Balance(tokenAddress, ownerAddress, network); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// tokenAddress, +// owner: ownerAddress, +// network, +// raw: balance.raw.toString(), +// formatted: balance.formatted, +// symbol: balance.token.symbol, +// decimals: balance.token.decimals +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching token balance: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // TRANSACTION TOOLS + +// // Get transaction by hash +// server.tool( +// "get_transaction", +// "Get detailed information about a specific transaction by its hash. Includes sender, recipient, value, data, and more.", +// { +// txHash: z.string().describe("The transaction hash to look up (e.g., '0x1234...')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ txHash, network = "ethereum" }) => { +// try { +// const tx = await services.getTransaction(txHash as Hash, network); + +// return { +// content: [{ +// type: "text", +// text: services.helpers.formatJson(tx) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching transaction ${txHash}: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Get transaction receipt +// server.tool( +// "get_transaction_receipt", +// "Get a transaction receipt by its hash", +// { +// txHash: z.string().describe("The transaction hash to look up"), +// network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ txHash, network = "ethereum" }) => { +// try { +// const receipt = await services.getTransactionReceipt(txHash as Hash, network); + +// return { +// content: [{ +// type: "text", +// text: services.helpers.formatJson(receipt) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching transaction receipt ${txHash}: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Estimate gas +// server.tool( +// "estimate_gas", +// "Estimate the gas cost for a transaction", +// { +// to: z.string().describe("The recipient address"), +// value: z.string().optional().describe("The amount of ETH to send in ether (e.g., '0.1')"), +// data: z.string().optional().describe("The transaction data as a hex string"), +// network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ to, value, data, network = "ethereum" }) => { +// try { +// const params: any = { to: to as Address }; + +// if (value) { +// params.value = services.helpers.parseEther(value); +// } + +// if (data) { +// params.data = data as `0x${string}`; +// } + +// const gas = await services.estimateGas(params, network); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// network, +// estimatedGas: gas.toString() +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error estimating gas: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // TRANSFER TOOLS + +// // Transfer ETH +// server.tool( +// "transfer_eth", +// "Transfer native tokens (ETH, MATIC, etc.) to an address", +// { +// privateKey: z.string().describe("Private key of the sender account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), +// to: z.string().describe("The recipient address or ENS name (e.g., '0x1234...' or 'vitalik.eth')"), +// amount: z.string().describe("Amount to send in ETH (or the native token of the network), as a string (e.g., '0.1')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") +// }, +// async ({ privateKey, to, amount, network = "ethereum" }) => { +// try { +// const txHash = await services.transferETH(privateKey, to, amount, network); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// success: true, +// txHash, +// to, +// amount, +// network +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error transferring ETH: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Transfer ERC20 +// server.tool( +// "transfer_erc20", +// "Transfer ERC20 tokens to another address", +// { +// privateKey: z.string().describe("Private key of the sending account (this is used for signing and is never stored)"), +// tokenAddress: z.string().describe("The address of the ERC20 token contract"), +// toAddress: z.string().describe("The recipient address"), +// amount: z.string().describe("The amount of tokens to send (in token units, e.g., '10' for 10 tokens)"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") +// }, +// async ({ privateKey, tokenAddress, toAddress, amount, network = "ethereum" }) => { +// try { +// // Get the formattedKey with 0x prefix +// const formattedKey = privateKey.startsWith('0x') +// ? privateKey as `0x${string}` +// : `0x${privateKey}` as `0x${string}`; + +// const result = await services.transferERC20( +// tokenAddress as Address, +// toAddress as Address, +// amount, +// formattedKey, +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// success: true, +// txHash: result.txHash, +// network, +// tokenAddress, +// recipient: toAddress, +// amount: result.amount.formatted, +// symbol: result.token.symbol +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error transferring ERC20 tokens: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Approve ERC20 token spending +// server.tool( +// "approve_token_spending", +// "Approve another address (like a DeFi protocol or exchange) to spend your ERC20 tokens. This is often required before interacting with DeFi protocols.", +// { +// privateKey: z.string().describe("Private key of the token owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), +// tokenAddress: z.string().describe("The contract address of the ERC20 token to approve for spending (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC on Ethereum)"), +// spenderAddress: z.string().describe("The contract address being approved to spend your tokens (e.g., a DEX or lending protocol)"), +// amount: z.string().describe("The amount of tokens to approve in token units, not wei (e.g., '1000' to approve spending 1000 tokens). Use a very large number for unlimited approval."), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ privateKey, tokenAddress, spenderAddress, amount, network = "ethereum" }) => { +// try { +// // Get the formattedKey with 0x prefix +// const formattedKey = privateKey.startsWith('0x') +// ? privateKey as `0x${string}` +// : `0x${privateKey}` as `0x${string}`; + +// const result = await services.approveERC20( +// tokenAddress as Address, +// spenderAddress as Address, +// amount, +// formattedKey, +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// success: true, +// txHash: result.txHash, +// network, +// tokenAddress, +// spender: spenderAddress, +// amount: result.amount.formatted, +// symbol: result.token.symbol +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error approving token spending: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Transfer NFT (ERC721) +// server.tool( +// "transfer_nft", +// "Transfer an NFT (ERC721 token) from one address to another. Requires the private key of the current owner for signing the transaction.", +// { +// privateKey: z.string().describe("Private key of the NFT owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), +// tokenAddress: z.string().describe("The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)"), +// tokenId: z.string().describe("The ID of the specific NFT to transfer (e.g., '1234')"), +// toAddress: z.string().describe("The recipient wallet address that will receive the NFT"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default.") +// }, +// async ({ privateKey, tokenAddress, tokenId, toAddress, network = "ethereum" }) => { +// try { +// // Get the formattedKey with 0x prefix +// const formattedKey = privateKey.startsWith('0x') +// ? privateKey as `0x${string}` +// : `0x${privateKey}` as `0x${string}`; + +// const result = await services.transferERC721( +// tokenAddress as Address, +// toAddress as Address, +// BigInt(tokenId), +// formattedKey, +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// success: true, +// txHash: result.txHash, +// network, +// collection: tokenAddress, +// tokenId: result.tokenId, +// recipient: toAddress, +// name: result.token.name, +// symbol: result.token.symbol +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error transferring NFT: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Transfer ERC1155 token +// server.tool( +// "transfer_erc1155", +// "Transfer ERC1155 tokens to another address. ERC1155 is a multi-token standard that can represent both fungible and non-fungible tokens in a single contract.", +// { +// privateKey: z.string().describe("Private key of the token owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), +// tokenAddress: z.string().describe("The contract address of the ERC1155 token collection (e.g., '0x76BE3b62873462d2142405439777e971754E8E77')"), +// tokenId: z.string().describe("The ID of the specific token to transfer (e.g., '1234')"), +// amount: z.string().describe("The quantity of tokens to send (e.g., '1' for a single NFT or '10' for 10 fungible tokens)"), +// toAddress: z.string().describe("The recipient wallet address that will receive the tokens"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. ERC1155 tokens exist across many networks. Defaults to Ethereum mainnet.") +// }, +// async ({ privateKey, tokenAddress, tokenId, amount, toAddress, network = "ethereum" }) => { +// try { +// // Get the formattedKey with 0x prefix +// const formattedKey = privateKey.startsWith('0x') +// ? privateKey as `0x${string}` +// : `0x${privateKey}` as `0x${string}`; + +// const result = await services.transferERC1155( +// tokenAddress as Address, +// toAddress as Address, +// BigInt(tokenId), +// amount, +// formattedKey, +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// success: true, +// txHash: result.txHash, +// network, +// contract: tokenAddress, +// tokenId: result.tokenId, +// amount: result.amount, +// recipient: toAddress +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error transferring ERC1155 tokens: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Transfer ERC20 tokens +// server.tool( +// "transfer_token", +// "Transfer ERC20 tokens to an address", +// { +// privateKey: z.string().describe("Private key of the sender account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), +// tokenAddress: z.string().describe("The contract address or ENS name of the ERC20 token to transfer (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC or 'uniswap.eth')"), +// toAddress: z.string().describe("The recipient address or ENS name that will receive the tokens (e.g., '0x1234...' or 'vitalik.eth')"), +// amount: z.string().describe("Amount of tokens to send as a string (e.g., '100' for 100 tokens). This will be adjusted for the token's decimals."), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") +// }, +// async ({ privateKey, tokenAddress, toAddress, amount, network = "ethereum" }) => { +// try { +// const result = await services.transferERC20( +// tokenAddress, +// toAddress, +// amount, +// privateKey, +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// success: true, +// txHash: result.txHash, +// tokenAddress, +// toAddress, +// amount: result.amount.formatted, +// symbol: result.token.symbol, +// network +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error transferring tokens: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // CONTRACT TOOLS + +// // Read contract +// server.tool( +// "read_contract", +// "Read data from a smart contract by calling a view/pure function. This doesn't modify blockchain state and doesn't require gas or signing.", +// { +// contractAddress: z.string().describe("The address of the smart contract to interact with"), +// abi: z.array(z.any()).describe("The ABI (Application Binary Interface) of the smart contract function, as a JSON array"), +// functionName: z.string().describe("The name of the function to call on the contract (e.g., 'balanceOf')"), +// args: z.array(z.any()).optional().describe("The arguments to pass to the function, as an array (e.g., ['0x1234...'])"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ contractAddress, abi, functionName, args = [], network = "ethereum" }) => { +// try { +// // Parse ABI if it's a string +// const parsedAbi = typeof abi === 'string' ? JSON.parse(abi) : abi; + +// const params = { +// address: contractAddress as Address, +// abi: parsedAbi, +// functionName, +// args +// }; + +// const result = await services.readContract(params, network); + +// return { +// content: [{ +// type: "text", +// text: services.helpers.formatJson(result) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error reading contract: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Write to contract +// server.tool( +// "write_contract", +// "Write data to a smart contract by calling a state-changing function. This modifies blockchain state and requires gas payment and transaction signing.", +// { +// contractAddress: z.string().describe("The address of the smart contract to interact with"), +// abi: z.array(z.any()).describe("The ABI (Application Binary Interface) of the smart contract function, as a JSON array"), +// functionName: z.string().describe("The name of the function to call on the contract (e.g., 'transfer')"), +// args: z.array(z.any()).describe("The arguments to pass to the function, as an array (e.g., ['0x1234...', '1000000000000000000'])"), +// privateKey: z.string().describe("Private key of the sending account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ contractAddress, abi, functionName, args, privateKey, network = "ethereum" }) => { +// try { +// // Parse ABI if it's a string +// const parsedAbi = typeof abi === 'string' ? JSON.parse(abi) : abi; + +// const contractParams: Record = { +// address: contractAddress as Address, +// abi: parsedAbi, +// functionName, +// args +// }; + +// const txHash = await services.writeContract( +// privateKey as Hex, +// contractParams, +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// network, +// transactionHash: txHash, +// message: "Contract write transaction sent successfully" +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error writing to contract: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Check if address is a contract +// server.tool( +// "is_contract", +// "Check if an address is a smart contract or an externally owned account (EOA)", +// { +// address: z.string().describe("The wallet or contract address or ENS name to check (e.g., '0x1234...' or 'uniswap.eth')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") +// }, +// async ({ address, network = "ethereum" }) => { +// try { +// const isContract = await services.isContract(address, network); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// address, +// network, +// isContract, +// type: isContract ? "Contract" : "Externally Owned Account (EOA)" +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error checking if address is a contract: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Get ERC20 token information +// server.tool( +// "get_token_info", +// "Get comprehensive information about an ERC20 token including name, symbol, decimals, total supply, and other metadata. Use this to analyze any token on EVM chains.", +// { +// tokenAddress: z.string().describe("The contract address of the ERC20 token (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC on Ethereum)"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ tokenAddress, network = "ethereum" }) => { +// try { +// const tokenInfo = await services.getERC20TokenInfo(tokenAddress as Address, network); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// address: tokenAddress, +// network, +// ...tokenInfo +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching token info: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Get ERC20 token balance +// server.tool( +// "get_token_balance_erc20", +// "Get ERC20 token balance for an address", +// { +// address: z.string().describe("The address to check balance for"), +// tokenAddress: z.string().describe("The ERC20 token contract address"), +// network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") +// }, +// async ({ address, tokenAddress, network = "ethereum" }) => { +// try { +// const balance = await services.getERC20Balance( +// tokenAddress as Address, +// address as Address, +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// address, +// tokenAddress, +// network, +// balance: { +// raw: balance.raw.toString(), +// formatted: balance.formatted, +// decimals: balance.token.decimals +// } +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching ERC20 balance for ${address}: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Get NFT (ERC721) information +// server.tool( +// "get_nft_info", +// "Get detailed information about a specific NFT (ERC721 token), including collection name, symbol, token URI, and current owner if available.", +// { +// tokenAddress: z.string().describe("The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)"), +// tokenId: z.string().describe("The ID of the specific NFT token to query (e.g., '1234')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default.") +// }, +// async ({ tokenAddress, tokenId, network = "ethereum" }) => { +// try { +// const nftInfo = await services.getERC721TokenMetadata( +// tokenAddress as Address, +// BigInt(tokenId), +// network +// ); + +// // Check ownership separately +// let owner = null; +// try { +// // This may fail if tokenId doesn't exist +// owner = await services.getPublicClient(network).readContract({ +// address: tokenAddress as Address, +// abi: [{ +// inputs: [{ type: 'uint256' }], +// name: 'ownerOf', +// outputs: [{ type: 'address' }], +// stateMutability: 'view', +// type: 'function' +// }], +// functionName: 'ownerOf', +// args: [BigInt(tokenId)] +// }); +// } catch (e) { +// // Ownership info not available +// } + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// contract: tokenAddress, +// tokenId, +// network, +// ...nftInfo, +// owner: owner || 'Unknown' +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching NFT info: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Check NFT ownership +// server.tool( +// "check_nft_ownership", +// "Check if an address owns a specific NFT", +// { +// tokenAddress: z.string().describe("The contract address or ENS name of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for BAYC or 'boredapeyachtclub.eth')"), +// tokenId: z.string().describe("The ID of the NFT to check (e.g., '1234')"), +// ownerAddress: z.string().describe("The wallet address or ENS name to check ownership against (e.g., '0x1234...' or 'vitalik.eth')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") +// }, +// async ({ tokenAddress, tokenId, ownerAddress, network = "ethereum" }) => { +// try { +// const isOwner = await services.isNFTOwner( +// tokenAddress, +// ownerAddress, +// BigInt(tokenId), +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// tokenAddress, +// tokenId, +// ownerAddress, +// network, +// isOwner, +// result: isOwner ? "Address owns this NFT" : "Address does not own this NFT" +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error checking NFT ownership: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Add tool for getting ERC1155 token URI +// server.tool( +// "get_erc1155_token_uri", +// "Get the metadata URI for an ERC1155 token (multi-token standard used for both fungible and non-fungible tokens). The URI typically points to JSON metadata about the token.", +// { +// tokenAddress: z.string().describe("The contract address of the ERC1155 token collection (e.g., '0x76BE3b62873462d2142405439777e971754E8E77')"), +// tokenId: z.string().describe("The ID of the specific token to query metadata for (e.g., '1234')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. ERC1155 tokens exist across many networks. Defaults to Ethereum mainnet.") +// }, +// async ({ tokenAddress, tokenId, network = "ethereum" }) => { +// try { +// const uri = await services.getERC1155TokenURI( +// tokenAddress as Address, +// BigInt(tokenId), +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// contract: tokenAddress, +// tokenId, +// network, +// uri +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching ERC1155 token URI: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Add tool for getting ERC721 NFT balance +// server.tool( +// "get_nft_balance", +// "Get the total number of NFTs owned by an address from a specific collection. This returns the count of NFTs, not individual token IDs.", +// { +// tokenAddress: z.string().describe("The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)"), +// ownerAddress: z.string().describe("The wallet address to check the NFT balance for (e.g., '0x1234...')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default.") +// }, +// async ({ tokenAddress, ownerAddress, network = "ethereum" }) => { +// try { +// const balance = await services.getERC721Balance( +// tokenAddress as Address, +// ownerAddress as Address, +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// collection: tokenAddress, +// owner: ownerAddress, +// network, +// balance: balance.toString() +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching NFT balance: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // Add tool for getting ERC1155 token balance +// server.tool( +// "get_erc1155_balance", +// "Get the balance of a specific ERC1155 token ID owned by an address. ERC1155 allows multiple tokens of the same ID, so the balance can be greater than 1.", +// { +// tokenAddress: z.string().describe("The contract address of the ERC1155 token collection (e.g., '0x76BE3b62873462d2142405439777e971754E8E77')"), +// tokenId: z.string().describe("The ID of the specific token to check the balance for (e.g., '1234')"), +// ownerAddress: z.string().describe("The wallet address to check the token balance for (e.g., '0x1234...')"), +// network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. ERC1155 tokens exist across many networks. Defaults to Ethereum mainnet.") +// }, +// async ({ tokenAddress, tokenId, ownerAddress, network = "ethereum" }) => { +// try { +// const balance = await services.getERC1155Balance( +// tokenAddress as Address, +// ownerAddress as Address, +// BigInt(tokenId), +// network +// ); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// contract: tokenAddress, +// tokenId, +// owner: ownerAddress, +// network, +// balance: balance.toString() +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error fetching ERC1155 token balance: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); + +// // WALLET TOOLS + +// // Get address from private key +// server.tool( +// "get_address_from_private_key", +// "Get the EVM address derived from a private key", +// { +// privateKey: z.string().describe("Private key in hex format (with or without 0x prefix). SECURITY: This is used only for address derivation and is not stored.") +// }, +// async ({ privateKey }) => { +// try { +// // Ensure the private key has 0x prefix +// const formattedKey = privateKey.startsWith('0x') ? privateKey as Hex : `0x${privateKey}` as Hex; + +// const address = services.getAddressFromPrivateKey(formattedKey); + +// return { +// content: [{ +// type: "text", +// text: JSON.stringify({ +// address, +// privateKey: "0x" + privateKey.replace(/^0x/, '') +// }, null, 2) +// }] +// }; +// } catch (error) { +// return { +// content: [{ +// type: "text", +// text: `Error deriving address from private key: ${error instanceof Error ? error.message : String(error)}` +// }], +// isError: true +// }; +// } +// } +// ); +// } \ No newline at end of file diff --git a/src/core/dynamic_tools.ts b/src/core/dynamic_tools.ts new file mode 100644 index 0000000..4a6ff60 --- /dev/null +++ b/src/core/dynamic_tools.ts @@ -0,0 +1,296 @@ +import { ethers } from 'ethers'; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import * as services from "./services/index.js"; +import { type Address } from 'viem'; +import { networkNameMap, endpointMap, scanApiKeyMap, getChain } from "./chains.js"; +import { createClient } from "../lib/supabase/server.js"; + +// TODO: this will get an object of data types +// for it to work we would have to re-compose the args into an array before calling .readContract +// const paramsArray = read.params.map((item: any) => { +// return ({[item.name]: z.string().describe(`${item.name} is ${item.type}`)}) +// }); +// const params = Object.assign({}, ...paramsArray); + +/** + * Register all EVM-related tools with the MCP server + * + * All tools that accept Ethereum addresses also support ENS names (e.g., 'vitalik.eth'). + * ENS names are automatically resolved to addresses using the Ethereum Name Service. + * + * @param server The MCP server instance + */ +export async function registerDynamicTools(server: McpServer) { + + const dynamicTools = await buildTools(process.env.PROTOCOL, process.env.CONTRACT!, process.env.CHAIN!); + + for(const tool in dynamicTools) { + console.log("TOOL:", tool); + console.log("TOOL OBJECT:", dynamicTools[tool]); + + server.tool( + dynamicTools[tool].name, + dynamicTools[tool].description, + dynamicTools[tool].parameters, + dynamicTools[tool].execute + ); + } +} + +async function buildTools(protocol: string | undefined, address: string, chain: string) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const tools: Record = {}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let _contracts: any[] = []; + + if (protocol) { + const supabase = await createClient(); + const { data: contracts, error } = await supabase.from("contracts").select().eq('protocol', protocol?.toLowerCase()); + _contracts = contracts || []; + + if (_contracts.length === 0 || error) { + console.error("Error fetching contracts:", error); + return tools; + } + } else if (address && chain) { + try { + const { name, abi, chainId } = await fetchABI(address, chain); + _contracts = [{ name, address, abi, chain_id: chainId }]; + } catch (error) { + console.error("Error fetching contract:", error); + return tools; + } + } + + for (const contract of _contracts) { + const parsedABI = JSON.parse(contract.abi); + const { read: reads, write: writes, signatures } = decodeABI(parsedABI); + + for (const read of reads) { + const readTool = buildReadTool(contract, read, parsedABI); + tools[read.fname] = readTool; + } + + for (const write of writes) { + const writeTool = buildWriteTool(contract, write); + tools[write.fname] = writeTool; + } + + // tools[`available_tools_${contract.name}`] = availableTools(contract.name, signatures); + tools[`available_tools`] = availableTools(contract.name, signatures); + + } + + return tools; +} + +async function fetchABI(address: string, chain: string) { + const chainId = networkNameMap[chain]; + const endpoint = endpointMap[chainId]; + const apiKey = scanApiKeyMap[chainId]; + + console.log(chain, chainId, endpoint, apiKey); + + if(!chainId) { + throw new Error("Invalid chain"); + } + + if (!endpoint) { + throw new Error("No endpoint found for chain"); + } + + if (!apiKey) { + throw new Error("No API key found for chain"); + } + + const url = `${endpoint}?module=contract&action=getabi&address=${address}&apikey=${apiKey}`; + + const resp = await fetch(url); + const abi = await resp.json(); + + return { name: "contract", abi: abi.result, chainId }; +} + +interface AbiInput { + name: string; + type: string; + index?: number; +} + +interface AbiFunction { + type: string; + name: string; + inputs: AbiInput[]; + stateMutability: "view" | "pure" | "nonpayable" | "payable"; +} + +function decodeABI(abi: AbiFunction[]) { + const read = []; + const write = []; + const signatures = []; + + for (const item of abi) { + if (item.type !== "function") continue + + const fn = item + const signature = `${fn.name}(${fn.inputs.map((i: AbiInput) => i.type).join(",")})` + const fname = fn.name; + const params = fn.inputs.map((input: AbiInput) => ({ + name: input.name || `arg-${input.index}`, + type: input.type, + })); + + if (fn.stateMutability === "view" || fn.stateMutability === "pure") { + read.push({ signature, fname, params }); + } else if (fn.stateMutability === "nonpayable" || fn.stateMutability === "payable") { + write.push({ signature, fname, params }); + } + + signatures.push(signature); + } + + return { read, write, signatures } +} + + +const buildReadTool = (contract: any, read: any, abi: any) => { + return { + name: `read_${read.fname}`, + description: `Read function named ${read.fname} with signature ${read.signature}`, + parameters: z.object({ + args: z.array(z.any()).optional().describe("The arguments to pass to the function, as an array (e.g., ['0x1234...', 123, true])"), + }), + execute: async ({ args = [] }) => { + try { + const params = { + address: contract.address as Address, + abi, + functionName: read.fname, + args: args + }; + + const network = getChain(contract.chain_id).name.replace(' ', '-'); + const result = await services.readContract(params, network); + + return { + content: [{ + type: "text", + text: services.helpers.formatJson(result) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error reading contract: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + }; +}; + + +const buildWriteTool = (contract: any, write: any) => { + return { + name: `write_${write.fname}`, + description: `Write function named ${write.fname} with signature ${write.signature}`, + parameters: z.object({ + args: z.array(z.any()).optional().describe("The arguments to pass to the function, as an array (e.g., ['0x1234...', 123, true])"), + }), + execute: async ({ args = [] }) => { + try { + const _args = args.map((arg: any) => arg.toString()); + + const txParams: Record = { + address: contract.address as Address, + functionName: write.fname, + args: _args, + }; + + const cInterface = new ethers.Interface([`function ${write.signature}`]); + const stringifiedArgs = _args; + const data = cInterface.encodeFunctionData(write.fname, stringifiedArgs); + + const tx = { + to: contract.address, + data, + chainId: contract.chain_id, + value: 0 // TODO: send value if the function is payable + }; + + return { + content: [{ + type: "text", + text: `${JSON.stringify(txParams, null, 2)}` + }], + data: [{ + type: "transaction", + transaction: tx + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error building transaction: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + } +}; + +const availableTools = (contract: any, signatures: any) => { + const _dynamicTools = signatures.join("\n"); + const _staticTools = [ + "get_chain_info", + "resolve_ens", + "get_supported_networks", + "get_block_by_number", + "get_latest_block", + "get_balance", + "get_erc20_balance", + "get_token_balance", + "get_transaction", + "get_transaction_receipt", + "estimate_gas", + "transfer_eth", + "transfer_erc20", + "approve_token_spending", + "transfer_nft", + "transfer_erc1155", + "transfer_token", + "read_contract", + "write_contract", + "is_contract", + "get_token_info", + "get_token_balance_erc20", + "get_nft_info", + "check_nft_ownership", + "get_erc1155_token_uri", + "get_nft_balance", + "get_erc1155_balance", + "get_address_from_private_key", + ].join("\n"); + + const availableTools = `${_dynamicTools}\n\n${_staticTools}`; + + return { + name: `available_tools_${contract.name}`, + description: `List the availables tools for ${contract.name}`, + parameters: z.object({}), + execute: async () => { + return { + content: [{ + type: "text", + text: availableTools + }], + isError: false + }; + } + }; +}; diff --git a/src/core/static_tools.ts b/src/core/static_tools.ts new file mode 100644 index 0000000..2031cdf --- /dev/null +++ b/src/core/static_tools.ts @@ -0,0 +1,546 @@ +import { ethers } from "ethers"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { getSupportedNetworks, getRpcUrl, networkNameMap } from "./chains.js"; +import * as services from "./services/index.js"; +import { type Address, type Hex, type Hash } from 'viem'; +import { normalize } from 'viem/ens'; + +/** + * Register all EVM-related tools with the MCP server + * + * All tools that accept Ethereum addresses also support ENS names (e.g., 'vitalik.eth'). + * ENS names are automatically resolved to addresses using the Ethereum Name Service. + * + * @param server The MCP server instance + */ +export function registerStaticTools(server: McpServer) { + // Get chain information + server.tool( + "get_chain_info", + "Get information about an EVM network", + { + network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") + }, + async ({ network = "ethereum" }) => { + try { + const chainId = await services.getChainId(network); + const blockNumber = await services.getBlockNumber(network); + const rpcUrl = getRpcUrl(network); + return { + content: [{ + type: "text", + text: JSON.stringify({ + network, + chainId, + blockNumber: blockNumber.toString(), + rpcUrl + }, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error fetching chain info: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + server.tool( + "resolve_ens", + "Resolve an ENS name to an Ethereum address", + { + ensName: z.string().describe("ENS name to resolve (e.g., 'vitalik.eth')"), + network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. ENS resolution works best on Ethereum mainnet. Defaults to Ethereum mainnet.") + }, + async ({ ensName, network = "ethereum" }) => { + try { + if (!ensName.includes('.')) { + return { + content: [{ + type: "text", + text: `Error: Input "${ensName}" is not a valid ENS name. ENS names must contain a dot (e.g., 'name.eth').` + }], + isError: true + }; + } + const normalizedEns = normalize(ensName); + const address = await services.resolveAddress(ensName, network); + return { + content: [{ + type: "text", + text: JSON.stringify({ + ensName: ensName, + normalizedName: normalizedEns, + resolvedAddress: address, + network + }, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error resolving ENS name: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + server.tool( + "get_supported_chains", + "Get a list of supported EVM networks", + {}, + async () => { + try { + const networks = getSupportedNetworks(); + return { + content: [{ + type: "text", + text: JSON.stringify({ + supportedNetworks: networks + }, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error fetching supported networks: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // BALANCE TOOLS + + // Get ETH balance + server.tool( + "get_balance", + "Get the native token balance (ETH, MATIC, etc.) for an address", + { + address: z.string().describe("The wallet address or ENS name (e.g., '0x1234...' or 'vitalik.eth') to check the balance for"), + network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") + }, + async ({ address, network = "ethereum" }) => { + try { + const balance = await services.getETHBalance(address, network); + + return { + content: [{ + type: "text", + text: JSON.stringify({ + address, + network, + wei: balance.wei.toString(), + ether: balance.ether + }, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error fetching balance: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // Get ERC20 balance + server.tool( + "get_erc20_balance", + "Get the ERC20 token balance of an Ethereum address", + { + address: z.string().describe("The Ethereum address to check"), + tokenAddress: z.string().describe("The ERC20 token contract address"), + network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") + }, + async ({ address, tokenAddress, network = "ethereum" }) => { + try { + const balance = await services.getERC20Balance( + tokenAddress as Address, + address as Address, + network + ); + + return { + content: [{ + type: "text", + text: JSON.stringify({ + address, + tokenAddress, + network, + balance: { + raw: balance.raw.toString(), + formatted: balance.formatted, + decimals: balance.token.decimals + } + }, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error fetching ERC20 balance for ${address}: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // TRANSACTION TOOLS + + // Get transaction by hash + server.tool( + "get_transaction", + "Get detailed information about a specific transaction by its hash. Includes sender, recipient, value, data, and more.", + { + txHash: z.string().describe("The transaction hash to look up (e.g., '0x1234...')"), + network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") + }, + async ({ txHash, network = "ethereum" }) => { + try { + const tx = await services.getTransaction(txHash as Hash, network); + + return { + content: [{ + type: "text", + text: services.helpers.formatJson(tx) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error fetching transaction ${txHash}: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // Get transaction receipt + server.tool( + "get_transaction_receipt", + "Get a transaction receipt by its hash", + { + txHash: z.string().describe("The transaction hash to look up"), + network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") + }, + async ({ txHash, network = "ethereum" }) => { + try { + const receipt = await services.getTransactionReceipt(txHash as Hash, network); + + return { + content: [{ + type: "text", + text: services.helpers.formatJson(receipt) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error fetching transaction receipt ${txHash}: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // Estimate gas + server.tool( + "estimate_gas", + "Estimate the gas cost for a transaction", + { + to: z.string().describe("The recipient address"), + value: z.string().optional().describe("The amount of ETH to send in ether (e.g., '0.1')"), + data: z.string().optional().describe("The transaction data as a hex string"), + network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") + }, + async ({ to, value, data, network = "ethereum" }) => { + try { + const params: any = { to: to as Address }; + + if (value) { + params.value = services.helpers.parseEther(value); + } + + if (data) { + params.data = data as `0x${string}`; + } + + const gas = await services.estimateGas(params, network); + + return { + content: [{ + type: "text", + text: JSON.stringify({ + network, + estimatedGas: gas.toString() + }, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error estimating gas: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // TRANSFER TOOLS + + // Transfer ETH + server.tool( + "transfer_eth", + "Transfer native tokens (ETH, MATIC, etc.) to an address", + { + to: z.string().describe("The recipient address or ENS name (e.g., '0x1234...' or 'vitalik.eth')"), + amount: z.string().describe("Amount to send in ETH (or the native token of the network), as a string (e.g., '0.1')"), + network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") + }, + async ({ to, amount, network = "ethereum" }) => { + try { + const txParams = { + to: to as Address, + value: amount, + chainId: network, + } + + const tx = { + to: to as Address, + value: amount, + chainId: networkNameMap[network], + data: "0x" + } + + return { + content: [{ + type: "text", + text: JSON.stringify(txParams, null, 2), + }], + data: [{ + type: "transaction", + transaction: tx + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error transferring ETH: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // Transfer ERC20 + server.tool( + "transfer_erc20", + "Transfer ERC20 tokens to another address", + { + tokenAddress: z.string().describe("The address of the ERC20 token contract"), + toAddress: z.string().describe("The recipient address"), + amount: z.string().describe("The amount of tokens to send (in token units, e.g., '10' for 10 tokens)"), + network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") + }, + async ({ tokenAddress, toAddress, amount, network = "ethereum" }) => { + try { + const txParams = { + token: tokenAddress, + to: toAddress, + amount, + network + } + + const cInterface = new ethers.Interface([`function transfer(address to, uint256 amount)`]); + const stringifiedArgs = [toAddress, amount]; + const data = cInterface.encodeFunctionData("transfer", stringifiedArgs); + + const chainId = networkNameMap[network]; + + const tx = { + to: toAddress, + data, + chainId, + value: 0, + }; + + return { + content: [{ + type: "text", + text: JSON.stringify(txParams, null, 2) + }], + data: [{ + type: "transaction", + transaction: tx + }] + } + } catch (error) { + return { + content: [{ + type: "text", + text: `Error transferring ERC20 tokens: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // Approve ERC20 token spending + server.tool( + "approve_token_spending", + "Approve another address (like a DeFi protocol or exchange) to spend your ERC20 tokens. This is often required before interacting with DeFi protocols.", + { + tokenAddress: z.string().describe("The contract address of the ERC20 token to approve for spending (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC on Ethereum)"), + spenderAddress: z.string().describe("The contract address being approved to spend your tokens (e.g., a DEX or lending protocol)"), + amount: z.string().describe("The amount of tokens to approve in token units, not wei (e.g., '1000' to approve spending 1000 tokens). Use a very large number for unlimited approval."), + network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") + }, + async ({ tokenAddress, spenderAddress, amount, network = "ethereum" }) => { + try { + + const txParams = { + token: tokenAddress, + spender: spenderAddress, + amount, + network + } + + const cInterface = new ethers.Interface([`function approve(address spender, uint256 amount)`]); + const stringifiedArgs = [spenderAddress, amount]; + const data = cInterface.encodeFunctionData("approve", stringifiedArgs); + + const chainId = networkNameMap[network]; + + const tx = { + to: tokenAddress, + data, + chainId, + value: 0, + }; + + return { + content: [{ + type: "text", + text: JSON.stringify(txParams, null, 2) + }], + data: [{ + type: "transaction", + transaction: tx + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error approving token spending: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // Get ERC20 token information + server.tool( + "get_token_info", + "Get comprehensive information about an ERC20 token including name, symbol, decimals, total supply, and other metadata. Use this to analyze any token on EVM chains.", + { + tokenAddress: z.string().describe("The contract address of the ERC20 token (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC on Ethereum)"), + network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") + }, + async ({ tokenAddress, network = "ethereum" }) => { + try { + const tokenInfo = await services.getERC20TokenInfo(tokenAddress as Address, network); + + return { + content: [{ + type: "text", + text: JSON.stringify({ + address: tokenAddress, + network, + ...tokenInfo + }, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error fetching token info: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + + // Get ERC20 token balance + server.tool( + "get_token_balance_erc20", + "Get ERC20 token balance for an address", + { + address: z.string().describe("The address to check balance for"), + tokenAddress: z.string().describe("The ERC20 token contract address"), + network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") + }, + async ({ address, tokenAddress, network = "ethereum" }) => { + try { + const balance = await services.getERC20Balance( + tokenAddress as Address, + address as Address, + network + ); + + return { + content: [{ + type: "text", + text: JSON.stringify({ + address, + tokenAddress, + network, + balance: { + raw: balance.raw.toString(), + formatted: balance.formatted, + decimals: balance.token.decimals + } + }, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error fetching ERC20 balance for ${address}: ${error instanceof Error ? error.message : String(error)}` + }], + isError: true + }; + } + } + ); + +} diff --git a/src/core/tools.ts b/src/core/tools.ts deleted file mode 100644 index a01d7a7..0000000 --- a/src/core/tools.ts +++ /dev/null @@ -1,1196 +0,0 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { z } from "zod"; -import { getSupportedNetworks, getRpcUrl } from "./chains.js"; -import * as services from "./services/index.js"; -import { type Address, type Hex, type Hash } from 'viem'; -import { normalize } from 'viem/ens'; - -/** - * Register all EVM-related tools with the MCP server - * - * All tools that accept Ethereum addresses also support ENS names (e.g., 'vitalik.eth'). - * ENS names are automatically resolved to addresses using the Ethereum Name Service. - * - * @param server The MCP server instance - */ -export function registerEVMTools(server: McpServer) { - // NETWORK INFORMATION TOOLS - - // Get chain information - server.tool( - "get_chain_info", - "Get information about an EVM network", - { - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") - }, - async ({ network = "ethereum" }) => { - try { - const chainId = await services.getChainId(network); - const blockNumber = await services.getBlockNumber(network); - const rpcUrl = getRpcUrl(network); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - network, - chainId, - blockNumber: blockNumber.toString(), - rpcUrl - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching chain info: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // ENS LOOKUP TOOL - - // Resolve ENS name to address - server.tool( - "resolve_ens", - "Resolve an ENS name to an Ethereum address", - { - ensName: z.string().describe("ENS name to resolve (e.g., 'vitalik.eth')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. ENS resolution works best on Ethereum mainnet. Defaults to Ethereum mainnet.") - }, - async ({ ensName, network = "ethereum" }) => { - try { - // Validate that the input is an ENS name - if (!ensName.includes('.')) { - return { - content: [{ - type: "text", - text: `Error: Input "${ensName}" is not a valid ENS name. ENS names must contain a dot (e.g., 'name.eth').` - }], - isError: true - }; - } - - // Normalize the ENS name - const normalizedEns = normalize(ensName); - - // Resolve the ENS name to an address - const address = await services.resolveAddress(ensName, network); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - ensName: ensName, - normalizedName: normalizedEns, - resolvedAddress: address, - network - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error resolving ENS name: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Get supported networks - server.tool( - "get_supported_networks", - "Get a list of supported EVM networks", - {}, - async () => { - try { - const networks = getSupportedNetworks(); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - supportedNetworks: networks - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching supported networks: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // BLOCK TOOLS - - // Get block by number - server.tool( - "get_block_by_number", - "Get a block by its block number", - { - blockNumber: z.number().describe("The block number to fetch"), - network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ blockNumber, network = "ethereum" }) => { - try { - const block = await services.getBlockByNumber(blockNumber, network); - - return { - content: [{ - type: "text", - text: services.helpers.formatJson(block) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching block ${blockNumber}: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Get latest block - server.tool( - "get_latest_block", - "Get the latest block from the EVM", - { - network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ network = "ethereum" }) => { - try { - const block = await services.getLatestBlock(network); - - return { - content: [{ - type: "text", - text: services.helpers.formatJson(block) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching latest block: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // BALANCE TOOLS - - // Get ETH balance - server.tool( - "get_balance", - "Get the native token balance (ETH, MATIC, etc.) for an address", - { - address: z.string().describe("The wallet address or ENS name (e.g., '0x1234...' or 'vitalik.eth') to check the balance for"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") - }, - async ({ address, network = "ethereum" }) => { - try { - const balance = await services.getETHBalance(address, network); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - address, - network, - wei: balance.wei.toString(), - ether: balance.ether - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching balance: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Get ERC20 balance - server.tool( - "get_erc20_balance", - "Get the ERC20 token balance of an Ethereum address", - { - address: z.string().describe("The Ethereum address to check"), - tokenAddress: z.string().describe("The ERC20 token contract address"), - network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ address, tokenAddress, network = "ethereum" }) => { - try { - const balance = await services.getERC20Balance( - tokenAddress as Address, - address as Address, - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - address, - tokenAddress, - network, - balance: { - raw: balance.raw.toString(), - formatted: balance.formatted, - decimals: balance.token.decimals - } - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching ERC20 balance for ${address}: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Get ERC20 token balance - server.tool( - "get_token_balance", - "Get the balance of an ERC20 token for an address", - { - tokenAddress: z.string().describe("The contract address or ENS name of the ERC20 token (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC or 'uniswap.eth')"), - ownerAddress: z.string().describe("The wallet address or ENS name to check the balance for (e.g., '0x1234...' or 'vitalik.eth')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") - }, - async ({ tokenAddress, ownerAddress, network = "ethereum" }) => { - try { - const balance = await services.getERC20Balance(tokenAddress, ownerAddress, network); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - tokenAddress, - owner: ownerAddress, - network, - raw: balance.raw.toString(), - formatted: balance.formatted, - symbol: balance.token.symbol, - decimals: balance.token.decimals - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching token balance: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // TRANSACTION TOOLS - - // Get transaction by hash - server.tool( - "get_transaction", - "Get detailed information about a specific transaction by its hash. Includes sender, recipient, value, data, and more.", - { - txHash: z.string().describe("The transaction hash to look up (e.g., '0x1234...')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ txHash, network = "ethereum" }) => { - try { - const tx = await services.getTransaction(txHash as Hash, network); - - return { - content: [{ - type: "text", - text: services.helpers.formatJson(tx) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching transaction ${txHash}: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Get transaction receipt - server.tool( - "get_transaction_receipt", - "Get a transaction receipt by its hash", - { - txHash: z.string().describe("The transaction hash to look up"), - network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ txHash, network = "ethereum" }) => { - try { - const receipt = await services.getTransactionReceipt(txHash as Hash, network); - - return { - content: [{ - type: "text", - text: services.helpers.formatJson(receipt) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching transaction receipt ${txHash}: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Estimate gas - server.tool( - "estimate_gas", - "Estimate the gas cost for a transaction", - { - to: z.string().describe("The recipient address"), - value: z.string().optional().describe("The amount of ETH to send in ether (e.g., '0.1')"), - data: z.string().optional().describe("The transaction data as a hex string"), - network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ to, value, data, network = "ethereum" }) => { - try { - const params: any = { to: to as Address }; - - if (value) { - params.value = services.helpers.parseEther(value); - } - - if (data) { - params.data = data as `0x${string}`; - } - - const gas = await services.estimateGas(params, network); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - network, - estimatedGas: gas.toString() - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error estimating gas: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // TRANSFER TOOLS - - // Transfer ETH - server.tool( - "transfer_eth", - "Transfer native tokens (ETH, MATIC, etc.) to an address", - { - privateKey: z.string().describe("Private key of the sender account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), - to: z.string().describe("The recipient address or ENS name (e.g., '0x1234...' or 'vitalik.eth')"), - amount: z.string().describe("Amount to send in ETH (or the native token of the network), as a string (e.g., '0.1')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") - }, - async ({ privateKey, to, amount, network = "ethereum" }) => { - try { - const txHash = await services.transferETH(privateKey, to, amount, network); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - success: true, - txHash, - to, - amount, - network - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error transferring ETH: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Transfer ERC20 - server.tool( - "transfer_erc20", - "Transfer ERC20 tokens to another address", - { - privateKey: z.string().describe("Private key of the sending account (this is used for signing and is never stored)"), - tokenAddress: z.string().describe("The address of the ERC20 token contract"), - toAddress: z.string().describe("The recipient address"), - amount: z.string().describe("The amount of tokens to send (in token units, e.g., '10' for 10 tokens)"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") - }, - async ({ privateKey, tokenAddress, toAddress, amount, network = "ethereum" }) => { - try { - // Get the formattedKey with 0x prefix - const formattedKey = privateKey.startsWith('0x') - ? privateKey as `0x${string}` - : `0x${privateKey}` as `0x${string}`; - - const result = await services.transferERC20( - tokenAddress as Address, - toAddress as Address, - amount, - formattedKey, - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - success: true, - txHash: result.txHash, - network, - tokenAddress, - recipient: toAddress, - amount: result.amount.formatted, - symbol: result.token.symbol - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error transferring ERC20 tokens: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Approve ERC20 token spending - server.tool( - "approve_token_spending", - "Approve another address (like a DeFi protocol or exchange) to spend your ERC20 tokens. This is often required before interacting with DeFi protocols.", - { - privateKey: z.string().describe("Private key of the token owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), - tokenAddress: z.string().describe("The contract address of the ERC20 token to approve for spending (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC on Ethereum)"), - spenderAddress: z.string().describe("The contract address being approved to spend your tokens (e.g., a DEX or lending protocol)"), - amount: z.string().describe("The amount of tokens to approve in token units, not wei (e.g., '1000' to approve spending 1000 tokens). Use a very large number for unlimited approval."), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ privateKey, tokenAddress, spenderAddress, amount, network = "ethereum" }) => { - try { - // Get the formattedKey with 0x prefix - const formattedKey = privateKey.startsWith('0x') - ? privateKey as `0x${string}` - : `0x${privateKey}` as `0x${string}`; - - const result = await services.approveERC20( - tokenAddress as Address, - spenderAddress as Address, - amount, - formattedKey, - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - success: true, - txHash: result.txHash, - network, - tokenAddress, - spender: spenderAddress, - amount: result.amount.formatted, - symbol: result.token.symbol - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error approving token spending: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Transfer NFT (ERC721) - server.tool( - "transfer_nft", - "Transfer an NFT (ERC721 token) from one address to another. Requires the private key of the current owner for signing the transaction.", - { - privateKey: z.string().describe("Private key of the NFT owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), - tokenAddress: z.string().describe("The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)"), - tokenId: z.string().describe("The ID of the specific NFT to transfer (e.g., '1234')"), - toAddress: z.string().describe("The recipient wallet address that will receive the NFT"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default.") - }, - async ({ privateKey, tokenAddress, tokenId, toAddress, network = "ethereum" }) => { - try { - // Get the formattedKey with 0x prefix - const formattedKey = privateKey.startsWith('0x') - ? privateKey as `0x${string}` - : `0x${privateKey}` as `0x${string}`; - - const result = await services.transferERC721( - tokenAddress as Address, - toAddress as Address, - BigInt(tokenId), - formattedKey, - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - success: true, - txHash: result.txHash, - network, - collection: tokenAddress, - tokenId: result.tokenId, - recipient: toAddress, - name: result.token.name, - symbol: result.token.symbol - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error transferring NFT: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Transfer ERC1155 token - server.tool( - "transfer_erc1155", - "Transfer ERC1155 tokens to another address. ERC1155 is a multi-token standard that can represent both fungible and non-fungible tokens in a single contract.", - { - privateKey: z.string().describe("Private key of the token owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), - tokenAddress: z.string().describe("The contract address of the ERC1155 token collection (e.g., '0x76BE3b62873462d2142405439777e971754E8E77')"), - tokenId: z.string().describe("The ID of the specific token to transfer (e.g., '1234')"), - amount: z.string().describe("The quantity of tokens to send (e.g., '1' for a single NFT or '10' for 10 fungible tokens)"), - toAddress: z.string().describe("The recipient wallet address that will receive the tokens"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. ERC1155 tokens exist across many networks. Defaults to Ethereum mainnet.") - }, - async ({ privateKey, tokenAddress, tokenId, amount, toAddress, network = "ethereum" }) => { - try { - // Get the formattedKey with 0x prefix - const formattedKey = privateKey.startsWith('0x') - ? privateKey as `0x${string}` - : `0x${privateKey}` as `0x${string}`; - - const result = await services.transferERC1155( - tokenAddress as Address, - toAddress as Address, - BigInt(tokenId), - amount, - formattedKey, - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - success: true, - txHash: result.txHash, - network, - contract: tokenAddress, - tokenId: result.tokenId, - amount: result.amount, - recipient: toAddress - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error transferring ERC1155 tokens: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Transfer ERC20 tokens - server.tool( - "transfer_token", - "Transfer ERC20 tokens to an address", - { - privateKey: z.string().describe("Private key of the sender account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), - tokenAddress: z.string().describe("The contract address or ENS name of the ERC20 token to transfer (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC or 'uniswap.eth')"), - toAddress: z.string().describe("The recipient address or ENS name that will receive the tokens (e.g., '0x1234...' or 'vitalik.eth')"), - amount: z.string().describe("Amount of tokens to send as a string (e.g., '100' for 100 tokens). This will be adjusted for the token's decimals."), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") - }, - async ({ privateKey, tokenAddress, toAddress, amount, network = "ethereum" }) => { - try { - const result = await services.transferERC20( - tokenAddress, - toAddress, - amount, - privateKey, - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - success: true, - txHash: result.txHash, - tokenAddress, - toAddress, - amount: result.amount.formatted, - symbol: result.token.symbol, - network - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error transferring tokens: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // CONTRACT TOOLS - - // Read contract - server.tool( - "read_contract", - "Read data from a smart contract by calling a view/pure function. This doesn't modify blockchain state and doesn't require gas or signing.", - { - contractAddress: z.string().describe("The address of the smart contract to interact with"), - abi: z.array(z.any()).describe("The ABI (Application Binary Interface) of the smart contract function, as a JSON array"), - functionName: z.string().describe("The name of the function to call on the contract (e.g., 'balanceOf')"), - args: z.array(z.any()).optional().describe("The arguments to pass to the function, as an array (e.g., ['0x1234...'])"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ contractAddress, abi, functionName, args = [], network = "ethereum" }) => { - try { - // Parse ABI if it's a string - const parsedAbi = typeof abi === 'string' ? JSON.parse(abi) : abi; - - const params = { - address: contractAddress as Address, - abi: parsedAbi, - functionName, - args - }; - - const result = await services.readContract(params, network); - - return { - content: [{ - type: "text", - text: services.helpers.formatJson(result) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error reading contract: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Write to contract - server.tool( - "write_contract", - "Write data to a smart contract by calling a state-changing function. This modifies blockchain state and requires gas payment and transaction signing.", - { - contractAddress: z.string().describe("The address of the smart contract to interact with"), - abi: z.array(z.any()).describe("The ABI (Application Binary Interface) of the smart contract function, as a JSON array"), - functionName: z.string().describe("The name of the function to call on the contract (e.g., 'transfer')"), - args: z.array(z.any()).describe("The arguments to pass to the function, as an array (e.g., ['0x1234...', '1000000000000000000'])"), - privateKey: z.string().describe("Private key of the sending account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored."), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ contractAddress, abi, functionName, args, privateKey, network = "ethereum" }) => { - try { - // Parse ABI if it's a string - const parsedAbi = typeof abi === 'string' ? JSON.parse(abi) : abi; - - const contractParams: Record = { - address: contractAddress as Address, - abi: parsedAbi, - functionName, - args - }; - - const txHash = await services.writeContract( - privateKey as Hex, - contractParams, - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - network, - transactionHash: txHash, - message: "Contract write transaction sent successfully" - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error writing to contract: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Check if address is a contract - server.tool( - "is_contract", - "Check if an address is a smart contract or an externally owned account (EOA)", - { - address: z.string().describe("The wallet or contract address or ENS name to check (e.g., '0x1234...' or 'uniswap.eth')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") - }, - async ({ address, network = "ethereum" }) => { - try { - const isContract = await services.isContract(address, network); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - address, - network, - isContract, - type: isContract ? "Contract" : "Externally Owned Account (EOA)" - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error checking if address is a contract: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Get ERC20 token information - server.tool( - "get_token_info", - "Get comprehensive information about an ERC20 token including name, symbol, decimals, total supply, and other metadata. Use this to analyze any token on EVM chains.", - { - tokenAddress: z.string().describe("The contract address of the ERC20 token (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC on Ethereum)"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ tokenAddress, network = "ethereum" }) => { - try { - const tokenInfo = await services.getERC20TokenInfo(tokenAddress as Address, network); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - address: tokenAddress, - network, - ...tokenInfo - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching token info: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Get ERC20 token balance - server.tool( - "get_token_balance_erc20", - "Get ERC20 token balance for an address", - { - address: z.string().describe("The address to check balance for"), - tokenAddress: z.string().describe("The ERC20 token contract address"), - network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.") - }, - async ({ address, tokenAddress, network = "ethereum" }) => { - try { - const balance = await services.getERC20Balance( - tokenAddress as Address, - address as Address, - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - address, - tokenAddress, - network, - balance: { - raw: balance.raw.toString(), - formatted: balance.formatted, - decimals: balance.token.decimals - } - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching ERC20 balance for ${address}: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Get NFT (ERC721) information - server.tool( - "get_nft_info", - "Get detailed information about a specific NFT (ERC721 token), including collection name, symbol, token URI, and current owner if available.", - { - tokenAddress: z.string().describe("The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)"), - tokenId: z.string().describe("The ID of the specific NFT token to query (e.g., '1234')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default.") - }, - async ({ tokenAddress, tokenId, network = "ethereum" }) => { - try { - const nftInfo = await services.getERC721TokenMetadata( - tokenAddress as Address, - BigInt(tokenId), - network - ); - - // Check ownership separately - let owner = null; - try { - // This may fail if tokenId doesn't exist - owner = await services.getPublicClient(network).readContract({ - address: tokenAddress as Address, - abi: [{ - inputs: [{ type: 'uint256' }], - name: 'ownerOf', - outputs: [{ type: 'address' }], - stateMutability: 'view', - type: 'function' - }], - functionName: 'ownerOf', - args: [BigInt(tokenId)] - }); - } catch (e) { - // Ownership info not available - } - - return { - content: [{ - type: "text", - text: JSON.stringify({ - contract: tokenAddress, - tokenId, - network, - ...nftInfo, - owner: owner || 'Unknown' - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching NFT info: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Check NFT ownership - server.tool( - "check_nft_ownership", - "Check if an address owns a specific NFT", - { - tokenAddress: z.string().describe("The contract address or ENS name of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for BAYC or 'boredapeyachtclub.eth')"), - tokenId: z.string().describe("The ID of the NFT to check (e.g., '1234')"), - ownerAddress: z.string().describe("The wallet address or ENS name to check ownership against (e.g., '0x1234...' or 'vitalik.eth')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', etc.) or chain ID. Supports all EVM-compatible networks. Defaults to Ethereum mainnet.") - }, - async ({ tokenAddress, tokenId, ownerAddress, network = "ethereum" }) => { - try { - const isOwner = await services.isNFTOwner( - tokenAddress, - ownerAddress, - BigInt(tokenId), - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - tokenAddress, - tokenId, - ownerAddress, - network, - isOwner, - result: isOwner ? "Address owns this NFT" : "Address does not own this NFT" - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error checking NFT ownership: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Add tool for getting ERC1155 token URI - server.tool( - "get_erc1155_token_uri", - "Get the metadata URI for an ERC1155 token (multi-token standard used for both fungible and non-fungible tokens). The URI typically points to JSON metadata about the token.", - { - tokenAddress: z.string().describe("The contract address of the ERC1155 token collection (e.g., '0x76BE3b62873462d2142405439777e971754E8E77')"), - tokenId: z.string().describe("The ID of the specific token to query metadata for (e.g., '1234')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. ERC1155 tokens exist across many networks. Defaults to Ethereum mainnet.") - }, - async ({ tokenAddress, tokenId, network = "ethereum" }) => { - try { - const uri = await services.getERC1155TokenURI( - tokenAddress as Address, - BigInt(tokenId), - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - contract: tokenAddress, - tokenId, - network, - uri - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching ERC1155 token URI: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Add tool for getting ERC721 NFT balance - server.tool( - "get_nft_balance", - "Get the total number of NFTs owned by an address from a specific collection. This returns the count of NFTs, not individual token IDs.", - { - tokenAddress: z.string().describe("The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)"), - ownerAddress: z.string().describe("The wallet address to check the NFT balance for (e.g., '0x1234...')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default.") - }, - async ({ tokenAddress, ownerAddress, network = "ethereum" }) => { - try { - const balance = await services.getERC721Balance( - tokenAddress as Address, - ownerAddress as Address, - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - collection: tokenAddress, - owner: ownerAddress, - network, - balance: balance.toString() - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching NFT balance: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // Add tool for getting ERC1155 token balance - server.tool( - "get_erc1155_balance", - "Get the balance of a specific ERC1155 token ID owned by an address. ERC1155 allows multiple tokens of the same ID, so the balance can be greater than 1.", - { - tokenAddress: z.string().describe("The contract address of the ERC1155 token collection (e.g., '0x76BE3b62873462d2142405439777e971754E8E77')"), - tokenId: z.string().describe("The ID of the specific token to check the balance for (e.g., '1234')"), - ownerAddress: z.string().describe("The wallet address to check the token balance for (e.g., '0x1234...')"), - network: z.string().optional().describe("Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. ERC1155 tokens exist across many networks. Defaults to Ethereum mainnet.") - }, - async ({ tokenAddress, tokenId, ownerAddress, network = "ethereum" }) => { - try { - const balance = await services.getERC1155Balance( - tokenAddress as Address, - ownerAddress as Address, - BigInt(tokenId), - network - ); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - contract: tokenAddress, - tokenId, - owner: ownerAddress, - network, - balance: balance.toString() - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error fetching ERC1155 token balance: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); - - // WALLET TOOLS - - // Get address from private key - server.tool( - "get_address_from_private_key", - "Get the EVM address derived from a private key", - { - privateKey: z.string().describe("Private key in hex format (with or without 0x prefix). SECURITY: This is used only for address derivation and is not stored.") - }, - async ({ privateKey }) => { - try { - // Ensure the private key has 0x prefix - const formattedKey = privateKey.startsWith('0x') ? privateKey as Hex : `0x${privateKey}` as Hex; - - const address = services.getAddressFromPrivateKey(formattedKey); - - return { - content: [{ - type: "text", - text: JSON.stringify({ - address, - privateKey: "0x" + privateKey.replace(/^0x/, '') - }, null, 2) - }] - }; - } catch (error) { - return { - content: [{ - type: "text", - text: `Error deriving address from private key: ${error instanceof Error ? error.message : String(error)}` - }], - isError: true - }; - } - } - ); -} \ No newline at end of file diff --git a/src/lib/supabase/server.ts b/src/lib/supabase/server.ts new file mode 100644 index 0000000..f4ffa08 --- /dev/null +++ b/src/lib/supabase/server.ts @@ -0,0 +1,8 @@ +import { createBrowserClient } from "@supabase/ssr"; + +export function createClient() { + return createBrowserClient( + process.env.SUPABASE_URL!, + process.env.SUPABASE_ANON_KEY!, + ); +} \ No newline at end of file diff --git a/src/server/server.ts b/src/server/server.ts index 81cf3a5..61c52f1 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -1,6 +1,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerEVMResources } from "../core/resources.js"; -import { registerEVMTools } from "../core/tools.js"; +import { registerStaticTools } from "../core/static_tools.js"; +import { registerDynamicTools } from "../core/dynamic_tools.js"; import { registerEVMPrompts } from "../core/prompts.js"; import { getSupportedNetworks } from "../core/chains.js"; @@ -15,7 +16,8 @@ async function startServer() { // Register all resources, tools, and prompts registerEVMResources(server); - registerEVMTools(server); + registerStaticTools(server); + await registerDynamicTools(server); registerEVMPrompts(server); // Log server information