diff --git a/package-lock.json b/package-lock.json index 56e1de7..72a3dc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,12 +18,31 @@ "radius-cli": "dist/index.js" }, "devDependencies": { + "@coinbase/cdp-sdk": "^1.51.0", + "@getpara/server-sdk": "3.0.0", + "@getpara/viem-v2-integration": "3.0.0", "@types/node": "^22.0.0", "typescript": "^5.6.0", "vitest": "^2.1.0" }, "engines": { "node": ">=20" + }, + "peerDependencies": { + "@coinbase/cdp-sdk": "^1.51.0", + "@getpara/server-sdk": "3.0.0", + "@getpara/viem-v2-integration": "3.0.0" + }, + "peerDependenciesMeta": { + "@coinbase/cdp-sdk": { + "optional": true + }, + "@getpara/server-sdk": { + "optional": true + }, + "@getpara/viem-v2-integration": { + "optional": true + } } }, "node_modules/@adraffy/ens-normalize": { @@ -32,6 +51,312 @@ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", "license": "MIT" }, + "node_modules/@celo/base": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@celo/base/-/base-7.0.4.tgz", + "integrity": "sha512-LUWVdqchXVKlp9h4Kh190wKE6DDy+zAREfVbKBIH/AWvrGv3EWQSUSDj3C8vrZjJ5wiCGt/ws+5+39Iao/W02Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@celo/utils": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@celo/utils/-/utils-8.0.3.tgz", + "integrity": "sha512-eHXSqRGWzXLGnfqq4eq37JAUnalqX5EIhlXyqSmxtIxc0Shkzlq7hrMpxI4diUS/T2zCcs9OQtr2ihjGxzqtqA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@celo/base": "^7.0.3", + "@ethereumjs/rlp": "^5.0.2", + "@ethereumjs/util": "8.0.5", + "@noble/ciphers": "1.1.3", + "@noble/curves": "1.3.0", + "@noble/hashes": "1.3.3", + "@types/bn.js": "^5.1.0", + "@types/node": "^18.7.16", + "bignumber.js": "^9.0.0", + "fp-ts": "2.16.9", + "io-ts": "2.0.1", + "web3-eth-abi": "1.10.4", + "web3-utils": "1.10.4" + } + }, + "node_modules/@celo/utils/node_modules/@ethereumjs/util": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.0.5.tgz", + "integrity": "sha512-259rXKK3b3D8HRVdRmlOEi6QFvwxdt304hhrEAmpZhsj7ufXEOTIc9JRZPMnXatKjECokdLNBcDOFBeBSzAIaw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@chainsafe/ssz": "0.9.4", + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^1.1.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@celo/utils/node_modules/@ethereumjs/util/node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "dev": true, + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@celo/utils/node_modules/@noble/ciphers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.1.3.tgz", + "integrity": "sha512-Ygv6WnWJHLLiW4fnNDC1z+i13bud+enXOFRBlpxI+NJliPWx5wdR+oWlTjLuBPTqjUjtHXtjkU6w3kuuH6upZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@celo/utils/node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@celo/utils/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@celo/utils/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@celo/utils/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@celo/utils/node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@celo/utils/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@celo/utils/node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@celo/utils/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@celo/utils/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/@celo/utils/node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@celo/utils/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@chainsafe/as-sha256": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz", + "integrity": "sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chainsafe/persistent-merkle-tree": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz", + "integrity": "sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chainsafe/as-sha256": "^0.3.1" + } + }, + "node_modules/@chainsafe/ssz": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.9.4.tgz", + "integrity": "sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chainsafe/as-sha256": "^0.3.1", + "@chainsafe/persistent-merkle-tree": "^0.4.2", + "case": "^1.6.3" + } + }, + "node_modules/@coinbase/cdp-sdk": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@coinbase/cdp-sdk/-/cdp-sdk-1.51.0.tgz", + "integrity": "sha512-XK8+OXDER1jirYpuiOct4ij65ODQ31LsmyRrZi/J7zF4GB89qxWZ0KPfAdsqJMP7VvE4no+Q++MKkQtAJUBoyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@solana-program/system": "^0.10.0", + "@solana-program/token": "^0.9.0", + "@solana/kit": "^5.5.1", + "abitype": "1.0.6", + "axios": "1.16.0", + "axios-retry": "^4.5.0", + "bs58": "^6.0.0", + "jose": "^6.2.0", + "md5": "^2.3.0", + "uncrypto": "^0.1.3", + "viem": "^2.47.0", + "zod": "^3.25.76" + } + }, + "node_modules/@coinbase/cdp-sdk/node_modules/abitype": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.6.tgz", + "integrity": "sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==", + "dev": true, + "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/@coinbase/cdp-sdk/node_modules/axios": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", + "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/@cosmjs/encoding": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.32.4.tgz", + "integrity": "sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -423,806 +748,2967 @@ "node": ">=12" } }, - "node_modules/@inquirer/ansi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", - "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", - "license": "MIT", + "node_modules/@ethereumjs/rlp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", + "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", + "dev": true, + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp.cjs" + }, "engines": { "node": ">=18" } }, - "node_modules/@inquirer/checkbox": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", - "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", - "license": "MIT", + "node_modules/@ethereumjs/util": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", + "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", + "dev": true, + "license": "MPL-2.0", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@ethereumjs/rlp": "^5.0.2", + "ethereum-cryptography": "^2.2.1" }, "engines": { "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } } }, - "node_modules/@inquirer/confirm": { - "version": "5.1.21", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", - "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "node_modules/@ethersproject/abi": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz", + "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true + "@ethersproject/address": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/hash": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/strings": "^5.8.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz", + "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/networks": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/transactions": "^5.8.0", + "@ethersproject/web": "^5.8.0" } }, - "node_modules/@inquirer/core": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", - "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "node_modules/@ethersproject/abstract-signer": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz", + "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", + "@ethersproject/abstract-provider": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz", + "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/rlp": "^5.8.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz", + "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz", + "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bignumber/node_modules/bn.js": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ethersproject/bytes": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", + "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz", + "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.8.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz", + "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-signer": "^5.8.0", + "@ethersproject/address": "^5.8.0", + "@ethersproject/base64": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/strings": "^5.8.0" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz", + "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", + "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT" + }, + "node_modules/@ethersproject/networks": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz", + "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", + "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz", + "integrity": "sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", + "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "bn.js": "^5.2.1", + "elliptic": "6.6.1", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key/node_modules/bn.js": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ethersproject/strings": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz", + "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz", + "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/rlp": "^5.8.0", + "@ethersproject/signing-key": "^5.8.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz", + "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/base64": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/strings": "^5.8.0" + } + }, + "node_modules/@getpara/core-sdk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@getpara/core-sdk/-/core-sdk-3.0.0.tgz", + "integrity": "sha512-8YPllcv8TeYrFhD988MtR4x3lBytrdi2kJc9Zjewh3KeZ4cIyS4vMqHj/YN6vIkL2XFr04JOcdcIU2RwSOOSSw==", + "dev": true, + "dependencies": { + "@celo/utils": "^8.0.2", + "@cosmjs/encoding": "^0.32.4", + "@ethereumjs/util": "^9.1.0", + "@getpara/user-management-client": "3.0.0", + "@noble/hashes": "^1.5.0", + "@opentelemetry/api": "^1.9.1", + "@opentelemetry/context-zone": "^2.7.1", + "@opentelemetry/core": "^2.7.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.215.0", + "@opentelemetry/instrumentation": "^0.215.0", + "@opentelemetry/instrumentation-fetch": "^0.215.0", + "@opentelemetry/instrumentation-xml-http-request": "^0.216.0", + "@opentelemetry/resources": "^2.7.0", + "@opentelemetry/sdk-trace-base": "^2.7.0", + "@opentelemetry/semantic-conventions": "^1.40.0", + "axios": "^1.8.4", + "base64url": "^3.0.1", + "elliptic": "^6.6.1", + "libphonenumber-js": "^1.11.7", + "node-forge": "^1.3.1", + "uuid": "^11.1.0", + "xstate": "^5.24.0" + } + }, + "node_modules/@getpara/core-sdk/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@getpara/server-sdk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@getpara/server-sdk/-/server-sdk-3.0.0.tgz", + "integrity": "sha512-rYDccjiqCCd8r7KEV9oq7mezRnG8Gh4Mrk0H9lDmWjDYJqPT7yz+NGJTw1ANFOO70577Jntusu/FPTjPYr6Nag==", + "dev": true, + "dependencies": { + "@getpara/core-sdk": "3.0.0", + "@getpara/user-management-client": "3.0.0", + "@getpara/viem-v2-integration": "3.0.0", + "uuid": "^11.1.0", + "ws": "^8.14.2" + } + }, + "node_modules/@getpara/shared": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@getpara/shared/-/shared-1.21.0.tgz", + "integrity": "sha512-dsO1XBsRSP8ZCmabsNrO49T4rdImNf6AXcy35e3JLj1rwsIvzvEEAmztEmzecC+PGHrRdD/pSVarluSbaxGqxg==", + "dev": true + }, + "node_modules/@getpara/user-management-client": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@getpara/user-management-client/-/user-management-client-3.0.0.tgz", + "integrity": "sha512-kDUYLsf6OID/J7ZMHBz3Q/4Y1pzjB1wuCO/cZVmCxnYk/c+Ffcrzsm7pEP3jNFAIjdKdu5LOX7fYZzmYP7Z0mw==", + "dev": true, + "dependencies": { + "@getpara/shared": "^1.21.0", + "@opentelemetry/api": "^1.9.1", + "axios": "^1.8.4", + "axios-retry": "^4.5.0", + "libphonenumber-js": "^1.11.7" + } + }, + "node_modules/@getpara/viem-v2-integration": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@getpara/viem-v2-integration/-/viem-v2-integration-3.0.0.tgz", + "integrity": "sha512-BcgnE7tgwgkLul4e+WpXjfXPqNa3qPG/7VjnrgE9yVBeAgnNxu8YzIA9kAlzkoAwiLLNQZUt9aZvHVNolr0P0Q==", + "dev": true, + "dependencies": { + "@getpara/core-sdk": "3.0.0" + }, + "peerDependencies": { + "viem": "^2.39.0" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "engines": { - "node": ">=18" + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@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.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/@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/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.215.0.tgz", + "integrity": "sha512-xrFlqhdhUyO8wSRn6DjE0145/HPWSJ5Nm0C7vWua6TdL/FSEAZvEyvdsa9CRXuxo9ebb7j/NEPhEcO62IJ0qUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/context-zone": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-zone/-/context-zone-2.7.1.tgz", + "integrity": "sha512-B42kO3zIMVbJ+wj5nlSkDvLF8cJY+7wDKLomHp10GL00nvUnhY67UQ/soZQgKR4dvPf8zTKbcONDsOiJLyRuXw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-zone-peer-dep": "2.7.1", + "zone.js": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^0.14.0 || ^0.15.0 || ^0.16.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + } + }, + "node_modules/@opentelemetry/context-zone-peer-dep": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-zone-peer-dep/-/context-zone-peer-dep-2.7.1.tgz", + "integrity": "sha512-QPLvl82Ds+W9Tjz0s4b8UDUK9YkCb3pvaur4JQdgHe+eph6Ii20NbiC+wsdnBtG17DTPhmZcFvWMcQXZFBgeVw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0", + "zone.js": "^0.10.2 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^0.14.0 || ^0.15.0 || ^0.16.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.215.0.tgz", + "integrity": "sha512-k4J9ISeGpb0Bm/wCrlcrbroMFTkiWMrdhNxQGrlktxLy127Yzd4/7nrTawn5d/ApktYTknvdixsE6++34Qfi1w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/otlp-exporter-base": "0.215.0", + "@opentelemetry/otlp-transformer": "0.215.0", + "@opentelemetry/resources": "2.7.0", + "@opentelemetry/sdk-trace-base": "2.7.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", + "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.0.tgz", + "integrity": "sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.0.tgz", + "integrity": "sha512-Yg9zEXJB50DLVLpsKPk7NmNqlPlS+OvqhJGh0A8oawIOTPOwlm4eXs9BMJV7L79lvEwI+dWtAj+YjTyddV336A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/resources": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.215.0.tgz", + "integrity": "sha512-SyJONuqypQ2xWdYMy99vF7JhZ2kDTGx4oRmM/jZV+kRtZ96JTnJmEINbIJgHz7Gnhtw0bimHwbPy/pguA5wpPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.215.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fetch": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fetch/-/instrumentation-fetch-0.215.0.tgz", + "integrity": "sha512-ljaUeeF5CB7RNaUn8f/uZddNigmlYGeZvXpKl8boa3upTYLOHtBlMFNAKJyO+h1lt1Im9Y1cgA30gVE64iHvCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/instrumentation": "0.215.0", + "@opentelemetry/sdk-trace-web": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fetch/node_modules/@opentelemetry/core": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", + "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation-xml-http-request": { + "version": "0.216.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-xml-http-request/-/instrumentation-xml-http-request-0.216.0.tgz", + "integrity": "sha512-poTqbEwnIMzgWVjZkw1mnXjm4OJsaZC0ufsKOxzu6Pjvyz9Ipt6vHg/2kWJSn7DlJOKb6Pzoz4bfElk6j51pOA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/instrumentation": "0.216.0", + "@opentelemetry/sdk-trace-web": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-xml-http-request/node_modules/@opentelemetry/api-logs": { + "version": "0.216.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.216.0.tgz", + "integrity": "sha512-KmGTgvxTJ0J01d4mOeX1wMV5NUTNf9HebIuOOGDfIn0a/IrnXIQbOnlylDyl9tkDv4h0DUpdI/GqCdLzfTkUXg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-xml-http-request/node_modules/@opentelemetry/instrumentation": { + "version": "0.216.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.216.0.tgz", + "integrity": "sha512-BrY0b2K81OLgwBcFxY2wKgPFhq4DpindT+S83++zquc5Rtb2SuYLMkujgDRWMgZQDz+OT+dfvPnMGADPuw4FDw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.216.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-xml-http-request/node_modules/@opentelemetry/sdk-trace-web": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-web/-/sdk-trace-web-2.7.1.tgz", + "integrity": "sha512-K806OouCSOjMd8Nr7+ZCq3QT22tdAzzS/7h8vprfiKjkgFQ99/dvwU8d12WJANA6D5Qtme65hyBAqAu9CkQuxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.215.0.tgz", + "integrity": "sha512-lHrfbmeLSmesGSkkHiqDwOzfaEMSWXdc7q6UoLfbW8byONCb+bE/zkAr0kapN4US1baT/2nbpNT7Cn9XoB96Vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/otlp-transformer": "0.215.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", + "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.215.0.tgz", + "integrity": "sha512-cWwBvaV+vkXHkSoTYR8hGw+AW03UlgTr6xtrUKOMeum3T+8vffYXIfXu6KY5MLu8O9QtoBKqaKWw9I5xoOepng==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.215.0", + "@opentelemetry/core": "2.7.0", + "@opentelemetry/resources": "2.7.0", + "@opentelemetry/sdk-logs": "0.215.0", + "@opentelemetry/sdk-metrics": "2.7.0", + "@opentelemetry/sdk-trace-base": "2.7.0", + "protobufjs": "^8.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", + "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.0.tgz", + "integrity": "sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.0.tgz", + "integrity": "sha512-Yg9zEXJB50DLVLpsKPk7NmNqlPlS+OvqhJGh0A8oawIOTPOwlm4eXs9BMJV7L79lvEwI+dWtAj+YjTyddV336A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/resources": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.215.0.tgz", + "integrity": "sha512-y3ucOmphzc4vgBTyIGchs+N/1rkACmoka8QalT2z1LBNM232Z17zMYayHcMl+dgMoOadZ0b72UZv7mDtqy1cFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.215.0", + "@opentelemetry/core": "2.7.0", + "@opentelemetry/resources": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", + "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.0.tgz", + "integrity": "sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.0.tgz", + "integrity": "sha512-Vd7h95av/LYRsAVN7wbprvvJnHkq7swMXAo7Uad0Uxf9jl6NSReLa0JNivrcc5BVIx/vl2t+cgdVQQbnVhsR9w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/resources": "2.7.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", + "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.0.tgz", + "integrity": "sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-web": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-web/-/sdk-trace-web-2.7.0.tgz", + "integrity": "sha512-WehQSom/hQO0uDVtYQV5O+UaTQU6UFMevYs0uE33bK/4abEyRHrIZF+3DGMmTaz08jQkCfaa0xTOpR873WKn1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/sdk-trace-base": "2.7.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-web/node_modules/@opentelemetry/core": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", + "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-web/node_modules/@opentelemetry/resources": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.0.tgz", + "integrity": "sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-web/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.0.tgz", + "integrity": "sha512-Yg9zEXJB50DLVLpsKPk7NmNqlPlS+OvqhJGh0A8oawIOTPOwlm4eXs9BMJV7L79lvEwI+dWtAj+YjTyddV336A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.0", + "@opentelemetry/resources": "2.7.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", + "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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/bip32/node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/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/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/@scure/bip39/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/@solana-program/system": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@solana-program/system/-/system-0.10.0.tgz", + "integrity": "sha512-Go+LOEZmqmNlfr+Gjy5ZWAdY5HbYzk2RBewD9QinEU/bBSzpFfzqDRT55JjFRBGJUvMgf3C2vfXEGT4i8DSI4g==", + "dev": true, + "license": "Apache-2.0", "peerDependencies": { - "@types/node": ">=18" + "@solana/kit": "^5.0" + } + }, + "node_modules/@solana-program/token": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@solana-program/token/-/token-0.9.0.tgz", + "integrity": "sha512-vnZxndd4ED4Fc56sw93cWZ2djEeeOFxtaPS8SPf5+a+JZjKA/EnKqzbE1y04FuMhIVrLERQ8uR8H2h72eZzlsA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "@solana/kit": "^5.0" + } + }, + "node_modules/@solana/accounts": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/accounts/-/accounts-5.5.1.tgz", + "integrity": "sha512-TfOY9xixg5rizABuLVuZ9XI2x2tmWUC/OoN556xwfDlhBHBjKfszicYYOyD6nbFmwTGYarCmyGIdteXxTXIdhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/editor": { - "version": "4.2.23", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", - "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "node_modules/@solana/addresses": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/addresses/-/addresses-5.5.1.tgz", + "integrity": "sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA==", + "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/external-editor": "^1.0.3", - "@inquirer/type": "^3.0.10" + "@solana/assertions": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/nominal-types": "5.5.1" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/expand": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", - "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "node_modules/@solana/assertions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/assertions/-/assertions-5.5.1.tgz", + "integrity": "sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q==", + "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@solana/errors": "5.5.1" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "node_modules/@solana/codecs": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs/-/codecs-5.5.1.tgz", + "integrity": "sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g==", + "dev": true, "license": "MIT", "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/options": "5.5.1" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/figures": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", - "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "node_modules/@solana/codecs-core": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-5.5.1.tgz", + "integrity": "sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw==", + "dev": true, "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1" + }, "engines": { - "node": ">=18" + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@inquirer/input": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", - "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "node_modules/@solana/codecs-data-structures": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-data-structures/-/codecs-data-structures-5.5.1.tgz", + "integrity": "sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w==", + "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@solana/codecs-core": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/errors": "5.5.1" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/number": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", - "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "node_modules/@solana/codecs-numbers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-5.5.1.tgz", + "integrity": "sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw==", + "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@solana/codecs-core": "5.5.1", + "@solana/errors": "5.5.1" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/password": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", - "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "node_modules/@solana/codecs-strings": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-5.5.1.tgz", + "integrity": "sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A==", + "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@solana/codecs-core": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/errors": "5.5.1" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "fastestsmallesttextencoderdecoder": "^1.0.22", + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "fastestsmallesttextencoderdecoder": { + "optional": true + }, + "typescript": { "optional": true } } }, - "node_modules/@inquirer/prompts": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", - "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "node_modules/@solana/errors": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-5.5.1.tgz", + "integrity": "sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg==", + "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.3.2", - "@inquirer/confirm": "^5.1.21", - "@inquirer/editor": "^4.2.23", - "@inquirer/expand": "^4.0.23", - "@inquirer/input": "^4.3.1", - "@inquirer/number": "^3.0.23", - "@inquirer/password": "^4.0.23", - "@inquirer/rawlist": "^4.1.11", - "@inquirer/search": "^3.2.2", - "@inquirer/select": "^4.4.2" + "chalk": "5.6.2", + "commander": "14.0.2" + }, + "bin": { + "errors": "bin/cli.mjs" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/errors/node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@solana/fast-stable-stringify": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/fast-stable-stringify/-/fast-stable-stringify-5.5.1.tgz", + "integrity": "sha512-Ni7s2FN33zTzhTFgRjEbOVFO+UAmK8qi3Iu0/GRFYK4jN696OjKHnboSQH/EacQ+yGqS54bfxf409wU5dsLLCw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/functional": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/functional/-/functional-5.5.1.tgz", + "integrity": "sha512-tTHoJcEQq3gQx5qsdsDJ0LEJeFzwNpXD80xApW9o/PPoCNimI3SALkZl+zNW8VnxRrV3l3yYvfHWBKe/X3WG3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/instruction-plans": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/instruction-plans/-/instruction-plans-5.5.1.tgz", + "integrity": "sha512-7z3CB7YMcFKuVvgcnNY8bY6IsZ8LG61Iytbz7HpNVGX2u1RthOs1tRW8luTzSG1MPL0Ox7afyAVMYeFqSPHnaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/instructions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/instructions/-/instructions-5.5.1.tgz", + "integrity": "sha512-h0G1CG6S+gUUSt0eo6rOtsaXRBwCq1+Js2a+Ps9Bzk9q7YHNFA75/X0NWugWLgC92waRp66hrjMTiYYnLBoWOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/keys": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/keys/-/keys-5.5.1.tgz", + "integrity": "sha512-KRD61cL7CRL+b4r/eB9dEoVxIf/2EJ1Pm1DmRYhtSUAJD2dJ5Xw8QFuehobOGm9URqQ7gaQl+Fkc1qvDlsWqKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@solana/assertions": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/nominal-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/kit": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/kit/-/kit-5.5.1.tgz", + "integrity": "sha512-irKUGiV2yRoyf+4eGQ/ZeCRxa43yjFEL1DUI5B0DkcfZw3cr0VJtVJnrG8OtVF01vT0OUfYOcUn6zJW5TROHvQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@solana/accounts": "5.5.1", + "@solana/addresses": "5.5.1", + "@solana/codecs": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/instruction-plans": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/offchain-messages": "5.5.1", + "@solana/plugin-core": "5.5.1", + "@solana/programs": "5.5.1", + "@solana/rpc": "5.5.1", + "@solana/rpc-api": "5.5.1", + "@solana/rpc-parsed-types": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-subscriptions": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/signers": "5.5.1", + "@solana/sysvars": "5.5.1", + "@solana/transaction-confirmation": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/rawlist": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", - "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "node_modules/@solana/nominal-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/nominal-types/-/nominal-types-5.5.1.tgz", + "integrity": "sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ==", + "dev": true, "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" - }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/search": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", - "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "node_modules/@solana/offchain-messages": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/offchain-messages/-/offchain-messages-5.5.1.tgz", + "integrity": "sha512-g+xHH95prTU+KujtbOzj8wn+C7ZNoiLhf3hj6nYq3MTyxOXtBEysguc97jJveUZG0K97aIKG6xVUlMutg5yxhw==", + "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/nominal-types": "5.5.1" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/select": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", - "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "node_modules/@solana/options": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/options/-/options-5.5.1.tgz", + "integrity": "sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A==", + "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@inquirer/type": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", - "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "node_modules/@solana/plugin-core": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/plugin-core/-/plugin-core-5.5.1.tgz", + "integrity": "sha512-VUZl30lDQFJeiSyNfzU1EjYt2QZvoBFKEwjn1lilUJw7KgqD5z7mbV7diJhT+dLFs36i0OsjXvq5kSygn8YJ3A==", + "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20.18.0" }, "peerDependencies": { - "@types/node": ">=18" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "typescript": { "optional": true } } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "node_modules/@solana/programs": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/programs/-/programs-5.5.1.tgz", + "integrity": "sha512-7U9kn0Jsx1NuBLn5HRTFYh78MV4XN145Yc3WP/q5BlqAVNlMoU9coG5IUTJIG847TUqC1lRto3Dnpwm6T4YRpA==", "dev": true, - "license": "MIT" - }, - "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.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" + "@solana/addresses": "5.5.1", + "@solana/errors": "5.5.1" }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "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" + "node": ">=20.18.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", - "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", - "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", - "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", - "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", - "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", - "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", - "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", - "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", - "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", - "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", - "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", - "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", - "cpu": [ - "loong64" - ], + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/promises": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/promises/-/promises-5.5.1.tgz", + "integrity": "sha512-T9lfuUYkGykJmppEcssNiCf6yiYQxJkhiLPP+pyAc2z84/7r3UVIb2tNJk4A9sucS66pzJnVHZKcZVGUUp6wzA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", - "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", - "cpu": [ - "ppc64" - ], + "node_modules/@solana/rpc": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc/-/rpc-5.5.1.tgz", + "integrity": "sha512-ku8zTUMrkCWci66PRIBC+1mXepEnZH/q1f3ck0kJZ95a06bOTl5KU7HeXWtskkyefzARJ5zvCs54AD5nxjQJ+A==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/fast-stable-stringify": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/rpc-api": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-transport-http": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", - "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", - "cpu": [ - "ppc64" - ], + "node_modules/@solana/rpc-api": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-api/-/rpc-api-5.5.1.tgz", + "integrity": "sha512-XWOQQPhKl06Vj0xi3RYHAc6oEQd8B82okYJ04K7N0Vvy3J4PN2cxeK7klwkjgavdcN9EVkYCChm2ADAtnztKnA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/rpc-parsed-types": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", - "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", - "cpu": [ - "riscv64" - ], + "node_modules/@solana/rpc-parsed-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-parsed-types/-/rpc-parsed-types-5.5.1.tgz", + "integrity": "sha512-HEi3G2nZqGEsa3vX6U0FrXLaqnUCg4SKIUrOe8CezD+cSFbRTOn3rCLrUmJrhVyXlHoQVaRO9mmeovk31jWxJg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", - "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", - "cpu": [ - "riscv64" - ], + "node_modules/@solana/rpc-spec": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-spec/-/rpc-spec-5.5.1.tgz", + "integrity": "sha512-m3LX2bChm3E3by4mQrH4YwCAFY57QBzuUSWqlUw7ChuZ+oLLOq7b2czi4i6L4Vna67j3eCmB3e+4tqy1j5wy7Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/rpc-spec-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", - "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", - "cpu": [ - "s390x" - ], + "node_modules/@solana/rpc-spec-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-spec-types/-/rpc-spec-types-5.5.1.tgz", + "integrity": "sha512-6OFKtRpIEJQs8Jb2C4OO8KyP2h2Hy1MFhatMAoXA+0Ik8S3H+CicIuMZvGZ91mIu/tXicuOOsNNLu3HAkrakrw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", - "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", - "cpu": [ - "x64" - ], + "node_modules/@solana/rpc-subscriptions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions/-/rpc-subscriptions-5.5.1.tgz", + "integrity": "sha512-CTMy5bt/6mDh4tc6vUJms9EcuZj3xvK0/xq8IQ90rhkpYvate91RjBP+egvjgSayUg9yucU9vNuUpEjz4spM7w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/fast-stable-stringify": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-subscriptions-api": "5.5.1", + "@solana/rpc-subscriptions-channel-websocket": "5.5.1", + "@solana/rpc-subscriptions-spec": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/subscribable": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", - "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", - "cpu": [ - "x64" - ], + "node_modules/@solana/rpc-subscriptions-api": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-api/-/rpc-subscriptions-api-5.5.1.tgz", + "integrity": "sha512-5Oi7k+GdeS8xR2ly1iuSFkAv6CZqwG0Z6b1QZKbEgxadE1XGSDrhM2cn59l+bqCozUWCqh4c/A2znU/qQjROlw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/rpc-subscriptions-spec": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", - "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", - "cpu": [ - "x64" - ], + "node_modules/@solana/rpc-subscriptions-channel-websocket": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-channel-websocket/-/rpc-subscriptions-channel-websocket-5.5.1.tgz", + "integrity": "sha512-7tGfBBrYY8TrngOyxSHoCU5shy86iA9SRMRrPSyBhEaZRAk6dnbdpmUTez7gtdVo0BCvh9nzQtUycKWSS7PnFQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/rpc-subscriptions-spec": "5.5.1", + "@solana/subscribable": "5.5.1", + "ws": "^8.19.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", - "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", - "cpu": [ - "arm64" - ], + "node_modules/@solana/rpc-subscriptions-channel-websocket/node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] + "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/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", - "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", - "cpu": [ - "arm64" - ], + "node_modules/@solana/rpc-subscriptions-spec": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-spec/-/rpc-subscriptions-spec-5.5.1.tgz", + "integrity": "sha512-iq+rGq5fMKP3/mKHPNB6MC8IbVW41KGZg83Us/+LE3AWOTWV1WT20KT2iH1F1ik9roi42COv/TpoZZvhKj45XQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/subscribable": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", - "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", - "cpu": [ - "ia32" - ], + "node_modules/@solana/rpc-transformers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-transformers/-/rpc-transformers-5.5.1.tgz", + "integrity": "sha512-OsWqLCQdcrRJKvHiMmwFhp9noNZ4FARuMkHT5us3ustDLXaxOjF0gfqZLnMkulSLcKt7TGXqMhBV+HCo7z5M8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-transport-http": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-transport-http/-/rpc-transport-http-5.5.1.tgz", + "integrity": "sha512-yv8GoVSHqEV0kUJEIhkdOVkR2SvJ6yoWC51cJn2rSV7plr6huLGe0JgujCmB7uZhhaLbcbP3zxXxu9sOjsi7Fg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "undici-types": "^7.19.2" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", - "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", - "cpu": [ - "x64" - ], + "node_modules/@solana/rpc-transport-http/node_modules/undici-types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.27.1.tgz", + "integrity": "sha512-NyfbU7cCMYYxzBT07eOv0/WR3L5j6vmza6sRlF2sDVCkNvsNaCcaFDGu0a4WqzE983tKuSk7YRTY2C+1krumMg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", - "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", - "cpu": [ - "x64" - ], + "node_modules/@solana/rpc-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-types/-/rpc-types-5.5.1.tgz", + "integrity": "sha512-bibTFQ7PbHJJjGJPmfYC2I+/5CRFS4O2p9WwbFraX1Keeel+nRrt/NBXIy8veP5AEn2sVJIyJPpWBRpCx1oATA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/nominal-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "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==", + "node_modules/@solana/signers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/signers/-/signers-5.5.1.tgz", + "integrity": "sha512-FY0IVaBT2kCAze55vEieR6hag4coqcuJ31Aw3hqRH7mv6sV8oqwuJmUrx+uFwOp1gwd5OEAzlv6N4hOOple4sQ==", + "dev": true, "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/offchain-messages": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", + "node_modules/@solana/subscribable": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/subscribable/-/subscribable-5.5.1.tgz", + "integrity": "sha512-9K0PsynFq0CsmK1CDi5Y2vUIJpCqkgSS5yfDN0eKPgHqEptLEaia09Kaxc90cSZDZU5mKY/zv1NBmB6Aro9zQQ==", + "dev": true, "license": "MIT", "dependencies": { - "@noble/curves": "~1.9.0", - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" + "@solana/errors": "5.5.1" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@scure/bip32/node_modules/@noble/curves": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", - "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "node_modules/@solana/sysvars": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/sysvars/-/sysvars-5.5.1.tgz", + "integrity": "sha512-k3Quq87Mm+geGUu1GWv6knPk0ALsfY6EKSJGw9xUJDHzY/RkYSBnh0RiOrUhtFm2TDNjOailg8/m0VHmi3reFA==", + "dev": true, "license": "MIT", "dependencies": { - "@noble/hashes": "1.8.0" + "@solana/accounts": "5.5.1", + "@solana/codecs": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/rpc-types": "5.5.1" }, "engines": { - "node": "^14.21.3 || >=16" + "node": ">=20.18.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@scure/bip32/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==", + "node_modules/@solana/transaction-confirmation": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/transaction-confirmation/-/transaction-confirmation-5.5.1.tgz", + "integrity": "sha512-j4mKlYPHEyu+OD7MBt3jRoX4ScFgkhZC6H65on4Fux6LMScgivPJlwnKoZMnsgxFgWds0pl+BYzSiALDsXlYtw==", + "dev": true, "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/rpc": "5.5.1", + "@solana/rpc-subscriptions": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, "engines": { - "node": "^14.21.3 || >=16" + "node": ">=20.18.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", + "node_modules/@solana/transaction-messages": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/transaction-messages/-/transaction-messages-5.5.1.tgz", + "integrity": "sha512-aXyhMCEaAp3M/4fP0akwBBQkFPr4pfwoC5CLDq999r/FUwDax2RE/h4Ic7h2Xk+JdcUwsb+rLq85Y52hq84XvQ==", + "dev": true, "license": "MIT", "dependencies": { - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/rpc-types": "5.5.1" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@scure/bip39/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==", + "node_modules/@solana/transactions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/transactions/-/transactions-5.5.1.tgz", + "integrity": "sha512-8hHtDxtqalZ157pnx6p8k10D7J/KY/biLzfgh9R09VNLLY3Fqi7kJvJCr7M2ik3oRll56pxhraAGCC9yIT6eOA==", + "dev": true, "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1" + }, "engines": { - "node": "^14.21.3 || >=16" + "node": ">=20.18.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@types/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, "node_modules/@types/estree": { @@ -1238,6 +3724,7 @@ "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1376,12 +3863,49 @@ } } }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/aes-js": { "version": "4.0.0-beta.5", "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", "license": "MIT" }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1416,6 +3940,119 @@ "node": ">=12" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz", + "integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/axios-retry": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz", + "integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "is-retry-allowed": "^2.2.0" + }, + "peerDependencies": { + "axios": "0.x || 1.x" + } + }, + "node_modules/base-x": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz", + "integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "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/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "base-x": "^5.0.0" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1426,6 +4063,30 @@ "node": ">=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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/case": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", + "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/chai": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", @@ -1443,12 +4104,35 @@ "node": ">=18" } }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/chardet": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", "license": "MIT" }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/check-error": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", @@ -1459,6 +4143,13 @@ "node": ">= 16" } }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -1486,6 +4177,19 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -1495,6 +4199,16 @@ "node": ">=18" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1523,12 +4237,73 @@ "node": ">=6" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "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==", + "dev": true, + "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/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", @@ -1536,6 +4311,35 @@ "dev": true, "license": "MIT" }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -1585,6 +4389,107 @@ "@types/estree": "^1.0.0" } }, + "node_modules/ethereum-bloom-filters": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.2.0.tgz", + "integrity": "sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.4.0" + } + }, + "node_modules/ethereum-bloom-filters/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography/node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography/node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/ethers": { "version": "6.16.0", "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz", @@ -1601,62 +4506,271 @@ ], "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" + "@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/@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/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "license": "MIT" + }, + "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/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fp-ts": { + "version": "2.16.9", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.9.tgz", + "integrity": "sha512-+I2+FnVB+tVaxcYyQkHUq7ZdKScaBlX53A41mxQtpIccsfyv8PzdzP7fzp2AY832T4aoK6UZ5WRX/ebGd8uZuQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" }, "engines": { - "node": ">=14.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" } }, - "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/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=12.0.0" + "node": ">= 0.4" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 6" } }, "node_modules/iconv-lite": { @@ -1675,6 +4789,46 @@ "url": "https://opencollective.com/express" } }, + "node_modules/import-in-the-middle": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.0.1.tgz", + "integrity": "sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.15.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/io-ts": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.0.1.tgz", + "integrity": "sha512-RezD+WcCfW4VkMkEcQWL/Nmy/nqsWTvTYg7oUmTGzglvSSV2P9h2z1PVeREPFf0GWNzruYleAt1XCMQZSg1xxQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "fp-ts": "^2.0.0" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1684,6 +4838,30 @@ "node": ">=8" } }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isows": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", @@ -1699,6 +4877,37 @@ "ws": "*" } }, + "node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/libphonenumber-js": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.5.tgz", + "integrity": "sha512-7/kRezHmQlMfO6pmvt34orO/g3j1C47k8FCBXFgj/mklTLwQdBca1LkhDK6RM8UyM6JqHFAIikMdkKkyfQy39A==", + "dev": true, + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", @@ -1716,6 +4925,79 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "dev": true, + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1751,6 +5033,38 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-forge": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "license": "MIT" + }, "node_modules/ox": { "version": "0.14.20", "resolved": "https://registry.npmjs.org/ox/-/ox-0.14.20.tgz", @@ -1867,6 +5181,61 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/protobufjs": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.5.0.tgz", + "integrity": "sha512-df1jWDPA5VIBNRtuAHjqr09f2qN5D4Vke1wYqOQg1XJ7ZDpA7BD6L7E4tyChgGRLB5hqk2m79Zsy0WHwV9a84A==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readonly-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", + "integrity": "sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + }, + "engines": { + "node": ">=9.3.0 || >=8.10.0 <9.0.0" + } + }, "node_modules/rollup": { "version": "4.60.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", @@ -1912,6 +5281,27 @@ "fsevents": "~2.3.2" } }, + "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==", + "dev": true, + "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", @@ -1987,6 +5377,20 @@ "node": ">=8" } }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -2043,6 +5447,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2051,6 +5456,13 @@ "node": ">=14.17" } }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "dev": true, + "license": "MIT" + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -2058,6 +5470,27 @@ "devOptional": true, "license": "MIT" }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/viem": { "version": "2.48.8", "resolved": "https://registry.npmjs.org/viem/-/viem-2.48.8.tgz", @@ -2142,6 +5575,7 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -2285,6 +5719,75 @@ } } }, + "node_modules/web3-eth-abi": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.10.4.tgz", + "integrity": "sha512-cZ0q65eJIkd/jyOlQPDjr8X4fU6CRL1eWgdLwbWEpo++MPU/2P4PFk5ZLAdye9T5Sdp+MomePPJ/gHjLMj2VfQ==", + "dev": true, + "license": "LGPL-3.0", + "dependencies": { + "@ethersproject/abi": "^5.6.3", + "web3-utils": "1.10.4" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", + "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", + "dev": true, + "license": "LGPL-3.0", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils/node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "dev": true, + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/web3-utils/node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/web3-utils/node_modules/bn.js": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", + "dev": true, + "license": "MIT" + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -2321,6 +5824,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -2337,6 +5841,17 @@ } } }, + "node_modules/xstate": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/xstate/-/xstate-5.32.0.tgz", + "integrity": "sha512-zsk73aWGmxn9z34P0kbiod5JwTvdYRW3+IDxITq8sd9+VWwMyW7BUzpplnYy9mIEXa6V8IMDv7Hy4m0mhT5+2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/xstate" + } + }, "node_modules/yoctocolors-cjs": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", @@ -2348,6 +5863,25 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "devOptional": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zone.js": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.16.2.tgz", + "integrity": "sha512-Eky7p2Z1Ig3NnbfodSPoARCjKBSTFMnE/ACsP1L/XJEfY4SdOFce19BsUCWVwL6K5ABZFy5J3bjcMWffX+YM3Q==", + "dev": true, + "license": "MIT", + "peer": true } } } diff --git a/package.json b/package.json index 90f8de8..7c1e98b 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,26 @@ "ethers": "^6.13.0", "viem": "^2.21.0" }, + "peerDependencies": { + "@coinbase/cdp-sdk": "^1.51.0", + "@getpara/server-sdk": "3.0.0", + "@getpara/viem-v2-integration": "3.0.0" + }, + "peerDependenciesMeta": { + "@coinbase/cdp-sdk": { + "optional": true + }, + "@getpara/server-sdk": { + "optional": true + }, + "@getpara/viem-v2-integration": { + "optional": true + } + }, "devDependencies": { + "@coinbase/cdp-sdk": "^1.51.0", + "@getpara/server-sdk": "3.0.0", + "@getpara/viem-v2-integration": "3.0.0", "@types/node": "^22.0.0", "typescript": "^5.6.0", "vitest": "^2.1.0" diff --git a/src/commands/wallet.ts b/src/commands/wallet.ts index d3f044d..beafa4b 100644 --- a/src/commands/wallet.ts +++ b/src/commands/wallet.ts @@ -2,9 +2,10 @@ import { Command } from 'commander'; import { confirm, password as promptPassword } from '@inquirer/prompts'; import { readFileSync } from 'node:fs'; import { + decodeErrorResult, encodeFunctionData, - formatEther, formatUnits, + formatEther, isAddress, parseEther, parseUnits, @@ -13,16 +14,26 @@ import { type Hex, } from 'viem'; import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; -import { resolveConfig, readPasswordless, writeCachedAddress, writePasswordless } from '../lib/config.js'; -import { keystoreExists, loadKeystorePrivateKey, saveKeystore } from '../lib/keystore.js'; +import { resolveConfig, writeCachedAddress, writePasswordless } from '../lib/config.js'; +import { keystoreExists, saveKeystore } from '../lib/keystore.js'; import { getOwnAddress, requireAccount } from '../lib/account.js'; import { makePublicClient, makeWalletClient } from '../lib/client.js'; import { coerceArg, parseCastSignature } from '../lib/signature.js'; import { formatUsd, formatUsdShort, jsonStringify } from '../lib/format.js'; +import { splitAggregateBalance } from '../lib/balance.js'; import { registerWalletX402 } from './walletX402.js'; +import { getProvider } from '../lib/providers/index.js'; import type { GlobalOptions } from '../types.js'; const SBC_DECIMALS = 6; +const TURNSTILE_SBC_RESERVE_RAW = parseUnits('0.1', SBC_DECIMALS); +const SOLIDITY_ERROR_ABI = [ + { + type: 'error', + name: 'Error', + inputs: [{ name: 'message', type: 'string' }], + }, +] as const; const ERC20_TRANSFER_ABI = [ { type: 'function', @@ -43,6 +54,19 @@ const ERC20_TRANSFER_ABI = [ }, ] as const; +type WalletConfig = ReturnType; + +interface WalletBalance { + address: Address; + aggregateWei: bigint; + nativeWei: bigint; + sbc: string; + sbcRawWei: bigint; + sbcError: string | null; + total: string; + rusd: string; +} + function readMessageArg(arg: string, raw: boolean): string | { raw: Hex } { const text = arg === '-' ? readFileSync(0, 'utf8') : arg; if (raw) { @@ -64,6 +88,73 @@ function normalizePrivateKey(input: string): Hex { return withPrefix as Hex; } +async function readWalletBalance(cfg: WalletConfig, address: Address): Promise { + const client = makePublicClient(cfg); + // eth_getBalance is the aggregate: token_balance x rate + raw_native. + // SBC is already included, so derive the raw-native (RUSD) remainder + // instead of summing. See splitAggregateBalance. + const aggregateWei = await client.getBalance({ address }); + + let sbc = '0'; + let sbcRawWei = 0n; + let sbcError: string | null = null; + try { + sbcRawWei = await client.readContract({ + address: cfg.sbcAddress!, + abi: ERC20_TRANSFER_ABI, + functionName: 'balanceOf', + args: [address], + }); + sbc = formatUnits(sbcRawWei, SBC_DECIMALS); + } catch (e) { + sbcError = e instanceof Error ? e.message : String(e); + } + + const { nativeWei } = splitAggregateBalance({ + aggregateWei, + sbcRaw: sbcRawWei, + sbcDecimals: SBC_DECIMALS, + }); + const rusd = formatEther(nativeWei); + const total = formatEther(aggregateWei); + + return { + address, + aggregateWei, + nativeWei, + sbc, + sbcRawWei, + sbcError, + total, + rusd, + }; +} + +function formatBalanceLine(balance: WalletBalance): string { + if (balance.sbcError) { + return `Balance: $${formatUsdShort(balance.total)} (SBC/RUSD breakdown unavailable)`; + } + return `Balance: $${formatUsdShort(balance.total)} ($${formatUsd(balance.sbc)} SBC + $${formatUsd(balance.rusd)} RUSD)`; +} + +async function printLoggedInProviderBalance(cfg: WalletConfig, opts: GlobalOptions): Promise { + if (opts.json || cfg.walletProvider === 'keystore') return; + + let address: Address; + try { + address = await getOwnAddress(cfg, opts.privateKey); + } catch { + return; + } + + try { + console.log(formatBalanceLine(await readWalletBalance(cfg, address))); + } catch (e) { + const message = e instanceof Error ? e.message : String(e); + console.log(`Balance: unavailable (${message})`); + } +} + async function readNewPassword(envPassword?: string): Promise { if (envPassword !== undefined) return envPassword; const first = await promptPassword({ message: 'New keystore password:', mask: '*' }); @@ -123,6 +214,47 @@ export function registerWallet(program: Command): void { console.log(`Address: ${address}`); }); + wallet + .command('login') + .description('Log in to the active wallet provider') + .option('--reset', 'clear saved credentials and session before logging in') + .action(async (subOpts: { reset?: boolean }, cmd) => { + const opts = cmd.optsWithGlobals() as GlobalOptions; + const cfg = resolveConfig(opts); + const provider = getProvider(cfg.walletProvider); + if (provider.login) { + await provider.login(cfg, { reset: subOpts.reset }); + await printLoggedInProviderBalance(cfg, opts); + } else { + console.log(`${cfg.walletProvider} provider does not require login.`); + } + }); + + wallet + .command('logout') + .description('Log out of the active wallet provider') + .action(async (_subOpts, cmd) => { + const opts = cmd.optsWithGlobals() as GlobalOptions; + const cfg = resolveConfig(opts); + const provider = getProvider(cfg.walletProvider); + if (provider.logout) { + await provider.logout(cfg); + } else { + console.log(`${cfg.walletProvider} provider does not support logout.`); + } + }); + + wallet + .command('status') + .description('Show the current wallet provider and account status') + .action(async (_subOpts, cmd) => { + const opts = cmd.optsWithGlobals() as GlobalOptions; + const cfg = resolveConfig(opts); + const provider = getProvider(cfg.walletProvider); + await provider.status(cfg, opts); + await printLoggedInProviderBalance(cfg, opts); + }); + wallet .command('address') .description('Print the address associated with the local account') @@ -143,6 +275,12 @@ export function registerWallet(program: Command): void { .action(async (_subOpts, cmd) => { const opts = cmd.optsWithGlobals() as GlobalOptions; const cfg = resolveConfig(opts); + const provider = getProvider(cfg.walletProvider); + if (!provider.exportPrivateKey) { + throw new Error( + `wallet export is not supported for the ${cfg.walletProvider} provider (remote key material is not exportable).`, + ); + } if (!opts.privateKey && !keystoreExists(cfg.keystorePath)) { throw new Error( `No keystore at ${cfg.keystorePath}. Run \`radius-cli wallet new\` or pass --private-key.`, @@ -158,9 +296,7 @@ export function registerWallet(program: Command): void { if (opts.privateKey) { pk = normalizePrivateKey(opts.privateKey); } else { - const password = cfg.password - ?? (readPasswordless() ? '' : await promptPassword({ message: 'Keystore password:', mask: '*' })); - pk = await loadKeystorePrivateKey(cfg.keystorePath, password); + pk = await provider.exportPrivateKey(cfg); } const address = privateKeyToAccount(pk).address; if (opts.json) { @@ -242,49 +378,25 @@ export function registerWallet(program: Command): void { address = await getOwnAddress(cfg, opts.privateKey); } - const client = makePublicClient(cfg); - const rusdWei = await client.getBalance({ address }); - const rusd = formatEther(rusdWei); - - let sbc = '0'; - let sbcRawWei = 0n; - let sbcError: string | null = null; - try { - sbcRawWei = await client.readContract({ - address: cfg.sbcAddress!, - abi: ERC20_TRANSFER_ABI, - functionName: 'balanceOf', - args: [address], - }); - sbc = formatUnits(sbcRawWei, SBC_DECIMALS); - } catch (e) { - sbcError = e instanceof Error ? e.message : String(e); - } - - const total = Number(rusd) + Number(sbc); + const balance = await readWalletBalance(cfg, address); if (opts.json) { console.log( jsonStringify({ address, - totalUsd: total, - sbc, - rusd, - sbcWei: sbcRawWei.toString(), - rusdWei: rusdWei.toString(), - sbcError, + totalUsd: Number(balance.total), + sbc: balance.sbc, + rusd: balance.rusd, + sbcWei: balance.sbcRawWei.toString(), + rusdWei: balance.nativeWei.toString(), + totalWei: balance.aggregateWei.toString(), + sbcError: balance.sbcError, }), ); return; } console.log(`Address: ${address}`); - if (sbcError) { - console.log(`Balance: $${rusd} ($${formatUsd(rusd)} RUSD; SBC unavailable)`); - } else { - console.log( - `Balance: $${formatUsdShort(total)} ($${formatUsd(sbc)} SBC + $${formatUsd(rusd)} RUSD)`, - ); - } + console.log(formatBalanceLine(balance)); }); wallet @@ -321,10 +433,7 @@ export function registerWallet(program: Command): void { return; } if (symbol === 'SBC') { - if (!cfg.sbcAddress) { - throw new Error('SBC contract address is not configured. Set RADIUS_SBC_ADDRESS or pass --sbc.'); - } - await sendErc20(cfg, cfg.sbcAddress, to, amount, SBC_DECIMALS, opts, wait, gas); + await sendSbc(cfg, to, amount, opts, wait, gas); return; } } @@ -359,6 +468,66 @@ function parseGasLimit(input: string | undefined): bigint | undefined { return value; } +function decodeSolidityErrorString(text: string): string | null { + if (!text.startsWith('0x08c379a0')) return null; + try { + const decoded = decodeErrorResult({ abi: SOLIDITY_ERROR_ABI, data: text as Hex }); + return decoded.errorName === 'Error' && typeof decoded.args[0] === 'string' + ? decoded.args[0] + : null; + } catch { + return null; + } +} + +function collectErrorText(value: unknown, seen = new WeakSet(), depth = 0): string[] { + if (depth > 5 || value === null || value === undefined) return []; + if (typeof value === 'string') return [value, decodeSolidityErrorString(value)].filter((v): v is string => !!v); + if (typeof value !== 'object') return [String(value)]; + + if (seen.has(value)) return []; + seen.add(value); + + const text: string[] = []; + if (value instanceof Error) { + text.push(value.message); + text.push(...collectErrorText(value.cause, seen, depth + 1)); + } + for (const child of Object.values(value as Record)) { + if (typeof child === 'string' || typeof child === 'object') { + text.push(...collectErrorText(child, seen, depth + 1)); + } + } + return text; +} + +function isTransferAmountExceedsBalanceError(error: unknown): boolean { + return collectErrorText(error).some((text) => text.includes('transfer amount exceeds balance')); +} + +function formatTurnstileSpendableBalanceError(args: { + balanceRaw: bigint; + attemptedRaw: bigint; +}): string { + const balance = formatUnits(args.balanceRaw, SBC_DECIMALS); + const attempted = formatUnits(args.attemptedRaw, SBC_DECIMALS); + const reserve = formatUnits(TURNSTILE_SBC_RESERVE_RAW, SBC_DECIMALS); + + const lines = [ + `SBC transfer failed: attempted to send ${attempted} SBC from a wallet with ${balance} SBC.`, + `On Radius, up to ${reserve} SBC can be reserved for Turnstile gas backing and may not be spendable by the sender.`, + ]; + + if (args.balanceRaw <= TURNSTILE_SBC_RESERVE_RAW) { + lines.push(`Top up the sender above ${reserve} SBC before sending SBC.`); + } else { + const spendable = formatUnits(args.balanceRaw - TURNSTILE_SBC_RESERVE_RAW, SBC_DECIMALS); + lines.push(`Estimated spendable SBC after the Turnstile reserve: ${spendable}. Reduce the amount or top up the sender.`); + } + + return lines.join('\n'); +} + async function sendNative( cfg: ReturnType, to: string, @@ -419,6 +588,37 @@ async function sendErc20( await reportTx(publicClient, hash, opts, wait); } +async function sendSbc( + cfg: ReturnType, + to: string, + amount: string, + opts: GlobalOptions, + wait: boolean, + gas: bigint | undefined, +): Promise { + if (!cfg.sbcAddress) { + throw new Error('SBC contract address is not configured. Set RADIUS_SBC_ADDRESS or pass --sbc.'); + } + try { + await sendErc20(cfg, cfg.sbcAddress, to, amount, SBC_DECIMALS, opts, wait, gas); + } catch (e) { + if (!isTransferAmountExceedsBalanceError(e)) throw e; + + const account = await requireAccount(cfg, opts.privateKey); + const publicClient = makePublicClient(cfg); + const balanceRaw = await publicClient.readContract({ + address: cfg.sbcAddress, + abi: ERC20_TRANSFER_ABI, + functionName: 'balanceOf', + args: [account.address], + }); + throw new Error(formatTurnstileSpendableBalanceError({ + balanceRaw, + attemptedRaw: parseUnits(amount, SBC_DECIMALS), + })); + } +} + async function sendCastForm( cfg: ReturnType, args: string[], diff --git a/src/commands/walletX402.ts b/src/commands/walletX402.ts index 85ba4b4..107a46f 100644 --- a/src/commands/walletX402.ts +++ b/src/commands/walletX402.ts @@ -1,6 +1,6 @@ import type { Command } from 'commander'; import { confirm } from '@inquirer/prompts'; -import { formatUnits, parseUnits, type Address } from 'viem'; +import { formatUnits, isAddress, parseUnits, type Address } from 'viem'; import { resolveConfig } from '../lib/config.js'; import { requireAccount } from '../lib/account.js'; import { makePublicClient } from '../lib/client.js'; @@ -20,6 +20,7 @@ import { import { decodePaymentResponse, encodePaymentHeader, + encodePermitPaymentHeader, networkIdForChain, parseChallenge, pickAccept, @@ -32,7 +33,20 @@ import { readBalance, signTransferAuthorization, } from '../lib/x402/eip3009.js'; +import { readPermitNonce, signEip2612Permit, signPermit } from '../lib/x402/eip2612.js'; +import { + buildPermit2PaymentPayload, + PERMIT2_ADDRESS, + randomPermit2Nonce, + signPermit2WitnessTransfer, +} from '../lib/x402/permit2.js'; import type { GlobalOptions } from '../types.js'; +import { + EXIT_USAGE, + EXIT_BALANCE, + EXIT_NETWORK, + EXIT_PAYMENT, +} from '../lib/exitCodes.js'; interface SubOptions { header?: string[]; @@ -90,7 +104,7 @@ async function runX402( const verb = verbArg.toLowerCase(); if (!isSupportedVerb(verb)) { process.stderr.write(`x402: unsupported verb '${verbArg}' (use one of ${SUPPORTED_VERBS.join(', ')})\n`); - process.exit(2); + process.exit(EXIT_USAGE); } const reqHeaders = parseHeaderArgs(subOpts.header); @@ -102,20 +116,27 @@ async function runX402( const initial = await runRequest(verb as HttpVerb, url, { headers: reqHeaders, body }); if (initial.status !== 402) { emit(initial, null, !!opts.json, !!subOpts.include); - process.exit(initial.status >= 400 ? 1 : 0); + process.exit(initial.status >= 400 ? EXIT_NETWORK : 0); } const cfg = resolveConfig(opts); + // Official Radius x402 v2 servers carry the challenge in a base64 + // PAYMENT-REQUIRED response header; older servers put JSON in the body. let challenge; + const paymentRequired = initial.headers.get('payment-required'); try { - const text = decodeBodyAsUtf8(initial.body) ?? ''; - challenge = parseChallenge(JSON.parse(text)); + if (paymentRequired) { + challenge = parseChallenge(JSON.parse(Buffer.from(paymentRequired, 'base64').toString('utf8'))); + } else { + const text = decodeBodyAsUtf8(initial.body) ?? ''; + challenge = parseChallenge(JSON.parse(text)); + } } catch (e) { process.stderr.write( - `x402: server returned 402 but the body is not a valid challenge: ${(e as Error).message}\n`, + `x402: server returned 402 but no valid challenge in ${paymentRequired ? 'PAYMENT-REQUIRED header' : 'body'}: ${(e as Error).message}\n`, ); process.stderr.write(safeBodyPreview(initial.body)); - process.exit(2); + process.exit(EXIT_PAYMENT); } const accept = pickAccept(challenge.accepts, cfg.chain.id); @@ -126,7 +147,7 @@ async function runX402( process.stderr.write( `x402: no compatible payment option. Wanted scheme=exact network=${networkIdForChain(cfg.chain.id)}; server offered: ${offered}\n`, ); - process.exit(1); + process.exit(EXIT_PAYMENT); } const account = await requireAccount(cfg, opts.privateKey); @@ -139,7 +160,7 @@ async function runX402( process.stderr.write( `x402: failed to read asset metadata at ${accept.asset}: ${(e as Error).message}\n`, ); - process.exit(1); + process.exit(EXIT_NETWORK); } const balance = await readBalance(client, accept.asset, account.address); @@ -151,13 +172,13 @@ async function runX402( process.stderr.write( `x402: insufficient balance. Need ${amountStr} ${symbol}, have ${balanceStr} ${symbol}.\n`, ); - process.exit(1); + process.exit(EXIT_BALANCE); } const decided = await decideAutoPay(subOpts, accept, asset.decimals); if (decided === 'refuse-no-tty') { writeChallengeSummary(accept, asset.decimals, asset.symbol, balanceStr); - process.exit(2); + process.exit(EXIT_USAGE); } if (decided === 'prompt') { const proceed = await confirm({ @@ -166,42 +187,140 @@ async function runX402( }); if (!proceed) { process.stderr.write('x402: payment declined.\n'); - process.exit(1); + process.exit(EXIT_PAYMENT); } } - const authorization = makeAuthorization({ - from: account.address, - to: accept.payTo, - value: accept.maxAmountRequired, - maxTimeoutSeconds: accept.maxTimeoutSeconds, - }); + // Radius servers settle SBC via EIP-2612 permit + transferFrom (SBC has no + // EIP-3009) and advertise it in the challenge's extra. Standard x402 servers + // get the EIP-3009 transferWithAuthorization path. + const settlementSpender = + accept.extra?.settlementMethod === 'permit-transferFrom' && + typeof accept.extra.settlementSpender === 'string' && + isAddress(accept.extra.settlementSpender) + ? (accept.extra.settlementSpender as Address) + : undefined; - let signature; - try { - signature = await signTransferAuthorization(account, { - asset: accept.asset, - chainId: cfg.chain.id, - name: asset.name, - version: asset.version, - authorization, + // Official Radius v2 settlement: Permit2 dual-signature flow. + const usePermit2 = accept.extra?.assetTransferMethod === 'permit2'; + + let paymentHeader: string; + if (usePermit2) { + try { + const eip2612Nonce = await readPermitNonce(client, accept.asset, account.address); + const deadline = BigInt( + Math.floor(Date.now() / 1000) + (accept.maxTimeoutSeconds ?? 300), + ); + const eip2612Signature = await signEip2612Permit(account, { + asset: accept.asset, + chainId: cfg.chain.id, + name: asset.name, + version: asset.version, + spender: PERMIT2_ADDRESS, + value: accept.maxAmountRequired, + nonce: eip2612Nonce, + deadline, + }); + const permit2Nonce = randomPermit2Nonce(); + const permit2Signature = await signPermit2WitnessTransfer(account, { + token: accept.asset, + chainId: cfg.chain.id, + amount: accept.maxAmountRequired, + payTo: accept.payTo, + nonce: permit2Nonce, + deadline, + }); + const challengeResource = + challenge.resource && typeof challenge.resource === 'object' + ? (challenge.resource as { url?: string; description?: string; mimeType?: string }) + : {}; + paymentHeader = buildPermit2PaymentPayload({ + chainId: cfg.chain.id, + resource: { ...challengeResource, url: challengeResource.url ?? url }, + accepted: accept.raw, + token: accept.asset, + amount: accept.maxAmountRequired, + owner: account.address, + payTo: accept.payTo, + permit2Signature, + permit2Nonce, + eip2612Signature, + eip2612Nonce, + deadline, + }); + } catch (e) { + process.stderr.write( + `x402: failed to sign permit2 payment: ${(e as Error).message}\n`, + ); + process.exit(EXIT_PAYMENT); + } + } else if (settlementSpender) { + try { + const nonce = await readPermitNonce(client, accept.asset, account.address); + const deadline = BigInt( + Math.floor(Date.now() / 1000) + (accept.maxTimeoutSeconds ?? 300), + ); + const permit = await signPermit(account, { + asset: accept.asset, + chainId: cfg.chain.id, + name: asset.name, + version: asset.version, + spender: settlementSpender, + value: accept.maxAmountRequired, + nonce, + deadline, + }); + paymentHeader = encodePermitPaymentHeader({ + x402Version: challenge.x402Version, + scheme: accept.scheme, + network: accept.network, + payload: permit, + }); + } catch (e) { + process.stderr.write( + `x402: failed to sign EIP-2612 permit: ${(e as Error).message}\n`, + ); + process.exit(EXIT_PAYMENT); + } + } else { + const authorization = makeAuthorization({ + from: account.address, + to: accept.payTo, + value: accept.maxAmountRequired, + maxTimeoutSeconds: accept.maxTimeoutSeconds, }); - } catch (e) { - process.stderr.write( - `x402: failed to sign EIP-3009 authorization (asset may not support transferWithAuthorization): ${(e as Error).message}\n`, - ); - process.exit(1); - } - const paymentHeader = encodePaymentHeader({ - x402Version: challenge.x402Version, - scheme: accept.scheme, - network: accept.network, - payload: { signature, authorization }, - }); + let signature; + try { + signature = await signTransferAuthorization(account, { + asset: accept.asset, + chainId: cfg.chain.id, + name: asset.name, + version: asset.version, + authorization, + }); + } catch (e) { + process.stderr.write( + `x402: failed to sign EIP-3009 authorization (asset may not support transferWithAuthorization): ${(e as Error).message}\n`, + ); + process.exit(EXIT_PAYMENT); + } + + paymentHeader = encodePaymentHeader({ + x402Version: challenge.x402Version, + scheme: accept.scheme, + network: accept.network, + accepted: accept.raw, + resource: challenge.resource, + payload: { signature, authorization }, + }); + } const retryHeaders = new Headers(reqHeaders); + // Send both header spellings: official Radius v2 servers read + // PAYMENT-SIGNATURE, older x402 servers read X-Payment. retryHeaders.set('x-payment', paymentHeader); + retryHeaders.set('payment-signature', paymentHeader); const retry = await runRequest(verb as HttpVerb, url, { headers: retryHeaders, @@ -215,12 +334,12 @@ async function runX402( process.stderr.write( 'x402: server redirected the paid request cross-origin; refusing to replay X-PAYMENT.\n', ); - process.exit(1); + process.exit(EXIT_NETWORK); } } let paymentResponse: PaymentResponseBody | null = null; - const xpr = retry.headers.get('x-payment-response'); + const xpr = retry.headers.get('payment-response') ?? retry.headers.get('x-payment-response'); if (xpr) { try { paymentResponse = decodePaymentResponse(xpr); } catch { /* ignore malformed */ } } @@ -245,11 +364,11 @@ async function runX402( if (opts.json) { console.log(jsonStringify(envelope(retry, { ...summary, paid: false }))); } - process.exit(1); + process.exit(EXIT_PAYMENT); } emit(retry, summary, !!opts.json, !!subOpts.include); - process.exit(retry.status >= 400 ? 1 : 0); + process.exit(retry.status >= 400 ? EXIT_NETWORK : 0); } type Decision = 'auto-pay' | 'prompt' | 'refuse-no-tty'; diff --git a/src/index.ts b/src/index.ts index 36235b9..23f2cc3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,15 @@ import { registerReceipt } from './commands/receipt.js'; import { registerStorage } from './commands/storage.js'; import { registerTx } from './commands/tx.js'; import { registerWallet } from './commands/wallet.js'; +import { readWalletProvider } from './lib/config.js'; +import { + CliError, + EXIT_GENERAL_ERROR, + EXIT_AUTH, + EXIT_BALANCE, + EXIT_CONFIG, + EXIT_NETWORK, +} from './lib/exitCodes.js'; const program = new Command(); @@ -19,6 +28,7 @@ program .option('--private-key ', 'sign with this key instead of the local keystore') .option('--sbc
', 'override the SBC token contract address') .option('--rusd
', 'override the RUSD ERC-20 contract address') + .option('--wallet ', 'wallet provider: keystore, cdp, para, privy, or proxy (default: keystore)') .option('--json', 'machine-readable JSON output'); registerWallet(program); @@ -31,10 +41,37 @@ registerNonce(program); async function main(): Promise { await program.parseAsync(process.argv); + const selectedWallet = program.opts().wallet ?? process.env.RADIUS_WALLET ?? readWalletProvider(); + if (selectedWallet === 'para') { + // Para's SDK can leave transport/telemetry handles alive after CLI work is done. + process.exit(0); + } +} + +function inferExitCode(msg: string): number { + const lower = msg.toLowerCase(); + if (lower.includes('not logged in') || lower.includes('unauthorized') || lower.includes('not configured')) + return EXIT_AUTH; + if (lower.includes('not configured') || lower.includes('missing') || lower.includes('must be one of')) + return EXIT_CONFIG; + if (lower.includes('insufficient balance') || lower.includes('transfer amount exceeds balance')) + return EXIT_BALANCE; + if (lower.includes('rpc request failed') || lower.includes('exec failed') || lower.includes('execution reverted')) + return EXIT_NETWORK; + return EXIT_GENERAL_ERROR; } main().catch((err: unknown) => { - const msg = err instanceof Error ? err.message : String(err); + let msg: string; + if (err instanceof Error) { + msg = err.message; + } else if (typeof err === 'object' && err !== null) { + const obj = err as Record; + msg = typeof obj['message'] === 'string' ? obj['message'] : JSON.stringify(err, null, 2); + } else { + msg = String(err); + } + const exitCode = err instanceof CliError ? err.exitCode : inferExitCode(msg); process.stderr.write(`error: ${msg}\n`); - process.exit(1); + process.exit(exitCode); }); diff --git a/src/lib/account.ts b/src/lib/account.ts index d038e39..9a5b12b 100644 --- a/src/lib/account.ts +++ b/src/lib/account.ts @@ -1,8 +1,6 @@ -import { password as promptPassword } from '@inquirer/prompts'; -import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; -import type { Hex, PrivateKeyAccount } from 'viem'; -import { keystoreExists, loadAccount, readKeystoreAddress, saveKeystore } from './keystore.js'; -import { readPasswordless, writeCachedAddress, writePasswordless } from './config.js'; +import { privateKeyToAccount } from 'viem/accounts'; +import type { Address, Hex, LocalAccount } from 'viem'; +import { getProvider } from './providers/index.js'; import type { ResolvedConfig } from '../types.js'; function normalizePrivateKey(input: string): Hex { @@ -14,63 +12,26 @@ function normalizePrivateKey(input: string): Hex { return withPrefix as Hex; } -async function resolvePassword(cfg: ResolvedConfig): Promise { - if (cfg.password !== undefined) return cfg.password; - if (readPasswordless()) return ''; - return await promptPassword({ message: 'Keystore password:', mask: '*' }); -} - -/** - * Generate a fresh keystore on first use. Uses RADIUS_PASSWORD if set, empty otherwise. - * An empty password means the keystore JSON is effectively unencrypted — the file mode - * (0o600) is the only protection. The notice on stderr makes that clear. - */ -async function autoCreateKeystore(cfg: ResolvedConfig): Promise { - const pk = generatePrivateKey(); - const password = cfg.password ?? ''; - const address = await saveKeystore(cfg.keystorePath, pk, password); - writeCachedAddress(address); - writePasswordless(password === ''); - - const lines = [ - `Created new keystore at ${cfg.keystorePath}`, - `Address: ${address}`, - ]; - if (password === '') { - lines.push( - 'No password set — keystore is effectively unencrypted (file mode 0o600).', - 'To rotate to a password-protected keystore: `radius-cli wallet new --force`', - ); - } - process.stderr.write(lines.join('\n') + '\n'); - - return privateKeyToAccount(pk); -} - -/** Returns a viem account. Auto-creates a keystore on first use; prompts for password if one exists. */ +/** Returns a viem account via the active provider. --private-key overrides any provider. */ export async function requireAccount( cfg: ResolvedConfig, privateKeyOpt: string | undefined, -): Promise { +): Promise { if (privateKeyOpt) { return privateKeyToAccount(normalizePrivateKey(privateKeyOpt)); } - if (!keystoreExists(cfg.keystorePath)) { - return await autoCreateKeystore(cfg); - } - const password = await resolvePassword(cfg); - return await loadAccount(cfg.keystorePath, password); + const provider = getProvider(cfg.walletProvider); + return await provider.getAccount(cfg); } -/** Returns just the local address — no password prompt. Auto-creates a keystore on first use. */ +/** Returns just the address via the active provider — no password prompt for keystore. */ export async function getOwnAddress( cfg: ResolvedConfig, privateKeyOpt: string | undefined, -): Promise<`0x${string}`> { +): Promise
{ if (privateKeyOpt) { return privateKeyToAccount(normalizePrivateKey(privateKeyOpt)).address; } - const addr = readKeystoreAddress(cfg.keystorePath); - if (addr) return addr; - return (await autoCreateKeystore(cfg)).address; + const provider = getProvider(cfg.walletProvider); + return await provider.getAddress(cfg); } diff --git a/src/lib/balance.ts b/src/lib/balance.ts new file mode 100644 index 0000000..2cdeccd --- /dev/null +++ b/src/lib/balance.ts @@ -0,0 +1,26 @@ +/** + * Split an aggregate `eth_getBalance` result into its SBC and raw-native parts. + * + * Radius RPC semantics change (2026-06): `eth_getBalance` now returns + * `token_balance × per_unit_exchange_rate + raw_native` — no floor rounding, + * no per-transaction cap. The SBC token balance is therefore already included + * in the native balance, so summing `eth_getBalance + balanceOf(SBC)` double + * counts. + * + * Assuming the stablecoin peg (1 SBC = 1 native unit = $1), the raw native + * (RUSD) portion is the aggregate minus the SBC balance scaled to wei, + * clamped at zero in case the exchange rate ever drifts below peg. + */ +export function splitAggregateBalance(args: { + /** result of eth_getBalance (wei, 18 decimals) */ + aggregateWei: bigint; + /** result of SBC balanceOf (token units) */ + sbcRaw: bigint; + /** SBC token decimals */ + sbcDecimals: number; +}): { totalWei: bigint; sbcAsWei: bigint; nativeWei: bigint } { + const scale = 10n ** BigInt(18 - args.sbcDecimals); + const sbcAsWei = args.sbcRaw * scale; + const nativeWei = args.aggregateWei > sbcAsWei ? args.aggregateWei - sbcAsWei : 0n; + return { totalWei: args.aggregateWei, sbcAsWei, nativeWei }; +} diff --git a/src/lib/config.ts b/src/lib/config.ts index a979479..e256ce0 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -3,12 +3,18 @@ import { homedir } from 'node:os'; import { join } from 'node:path'; import { isAddress, type Address } from 'viem'; import { chainFor, DEFAULT_SBC_ADDRESS } from './chains.js'; -import type { GlobalOptions, NetworkName, ResolvedConfig } from '../types.js'; +import type { GlobalOptions, NetworkName, ResolvedConfig, WalletProviderName } from '../types.js'; + +const VALID_WALLET_PROVIDERS: WalletProviderName[] = ['keystore', 'cdp', 'para', 'privy', 'proxy']; const RADIUS_DIR = process.env.RADIUS_HOME ?? join(homedir(), '.radius'); const CONFIG_PATH = join(RADIUS_DIR, 'config.json'); const DEFAULT_KEYSTORE_PATH = join(RADIUS_DIR, 'keystore.json'); +interface ProviderConfig { + [key: string]: string | undefined; +} + interface FileConfig { network?: NetworkName; rpcUrl?: string; @@ -16,6 +22,8 @@ interface FileConfig { rusdAddress?: string; cachedAddress?: string; passwordless?: boolean; + wallet?: WalletProviderName; + providers?: Record; } function readFileConfig(): FileConfig { @@ -56,7 +64,15 @@ export function resolveConfig(opts: GlobalOptions): ResolvedConfig { const keystorePath = process.env.RADIUS_KEYSTORE_PATH ?? DEFAULT_KEYSTORE_PATH; const password = process.env.RADIUS_PASSWORD; - return { network, chain, rpcUrl, sbcAddress, rusdAddress, keystorePath, password }; + const walletRaw = opts.wallet ?? process.env.RADIUS_WALLET ?? file.wallet ?? 'keystore'; + if (!VALID_WALLET_PROVIDERS.includes(walletRaw as WalletProviderName)) { + throw new Error( + `--wallet must be one of ${VALID_WALLET_PROVIDERS.join(', ')} (got '${walletRaw}')`, + ); + } + const walletProvider = walletRaw as WalletProviderName; + + return { network, chain, rpcUrl, sbcAddress, rusdAddress, keystorePath, password, walletProvider }; } export function configPath(): string { @@ -93,3 +109,34 @@ export function writePasswordless(passwordless: boolean): void { else delete file.passwordless; writeFileConfig(file); } + +export function readWalletProvider(): WalletProviderName { + return readFileConfig().wallet ?? 'keystore'; +} + +export function writeWalletProvider(provider: WalletProviderName): void { + const file = readFileConfig(); + if (provider === 'keystore') delete file.wallet; + else file.wallet = provider; + writeFileConfig(file); +} + +export function readProviderConfig(provider: string): ProviderConfig { + return readFileConfig().providers?.[provider] ?? {}; +} + +export function writeProviderConfig(provider: string, update: Partial): void { + const file = readFileConfig(); + if (!file.providers) file.providers = {}; + file.providers[provider] = { ...file.providers[provider], ...update }; + writeFileConfig(file); +} + +export function deleteProviderConfig(provider: string): void { + const file = readFileConfig(); + if (file.providers) { + delete file.providers[provider]; + if (Object.keys(file.providers).length === 0) delete file.providers; + } + writeFileConfig(file); +} diff --git a/src/lib/exitCodes.ts b/src/lib/exitCodes.ts new file mode 100644 index 0000000..6b3ffaa --- /dev/null +++ b/src/lib/exitCodes.ts @@ -0,0 +1,40 @@ +/** + * Process exit codes for agent-friendly error handling. + * + * Agents can switch on the exit code to determine the failure category + * without parsing error text. + */ + +/** Command completed successfully. */ +export const EXIT_SUCCESS = 0; + +/** General / uncategorized error. */ +export const EXIT_GENERAL_ERROR = 1; + +/** Invalid CLI usage: bad arguments, unsupported verb, missing required input. */ +export const EXIT_USAGE = 2; + +/** Configuration error: missing env var, invalid config file, bad provider setup. */ +export const EXIT_CONFIG = 3; + +/** Authentication / authorization error: not logged in, invalid key, wallet not found. */ +export const EXIT_AUTH = 4; + +/** Network / RPC error: unreachable endpoint, transaction failed, exec reverted. */ +export const EXIT_NETWORK = 5; + +/** Insufficient balance for the requested operation. */ +export const EXIT_BALANCE = 6; + +/** Payment declined or failed (x402 specific). */ +export const EXIT_PAYMENT = 7; + +export class CliError extends Error { + readonly exitCode: number; + + constructor(message: string, exitCode: number) { + super(message); + this.name = 'CliError'; + this.exitCode = exitCode; + } +} diff --git a/src/lib/providerTelemetry.ts b/src/lib/providerTelemetry.ts new file mode 100644 index 0000000..41c3c56 --- /dev/null +++ b/src/lib/providerTelemetry.ts @@ -0,0 +1,21 @@ +import type { WalletProviderName } from '../types.js'; + +function setEnvDefault(key: string, value: string): void { + process.env[key] ??= value; +} + +export function disableProviderTelemetry(provider: WalletProviderName): void { + switch (provider) { + case 'cdp': + setEnvDefault('DISABLE_CDP_ERROR_REPORTING', 'true'); + setEnvDefault('DISABLE_CDP_USAGE_TRACKING', 'true'); + break; + case 'para': + setEnvDefault('OTEL_SDK_DISABLED', 'true'); + setEnvDefault('OTEL_TRACES_EXPORTER', 'none'); + break; + case 'privy': + case 'keystore': + break; + } +} diff --git a/src/lib/providers/cdp.ts b/src/lib/providers/cdp.ts new file mode 100644 index 0000000..d85a89b --- /dev/null +++ b/src/lib/providers/cdp.ts @@ -0,0 +1,273 @@ +import { input, password as promptPassword, select } from '@inquirer/prompts'; +import { type Address, type Hex, type LocalAccount } from 'viem'; +import { toAccount } from 'viem/accounts'; +import { readProviderConfig, writeProviderConfig, deleteProviderConfig } from '../config.js'; +import { jsonStringify } from '../format.js'; +import { disableProviderTelemetry } from '../providerTelemetry.js'; +import { signLegacyTransaction } from './remoteSigning.js'; +import { deleteProviderSession, providerSessionPath, readProviderSession, writeProviderSession } from './session.js'; +import type { ResolvedConfig, GlobalOptions } from '../../types.js'; +import type { WalletProvider } from './types.js'; + +const SESSION_FILE = 'cdp-session.json'; + +interface CdpSession { + address: string; + accountName?: string; +} + +function readSession(): CdpSession | null { + return readProviderSession(SESSION_FILE); +} + +function writeSession(session: CdpSession): void { + writeProviderSession(SESSION_FILE, session); +} + +function deleteSession(): void { + deleteProviderSession(SESSION_FILE); +} + +async function loadCdpSDK() { + disableProviderTelemetry('cdp'); + try { + const { CdpClient } = await import('@coinbase/cdp-sdk'); + return { CdpClient }; + } catch { + throw new Error( + 'CDP SDK package is not installed. Run:\n' + + ' npm install @coinbase/cdp-sdk\n' + + 'to enable the CDP wallet provider.', + ); + } +} + +interface CdpCredentials { + apiKeyId: string; + apiKeySecret: string; + walletSecret: string; +} + +async function resolveCredentials(opts: { interactive?: boolean } = {}): Promise { + // env → config → prompt (if interactive) → error + const config = readProviderConfig('cdp'); + const apiKeyId = process.env.CDP_API_KEY_ID ?? config.apiKeyId; + const apiKeySecret = process.env.CDP_API_KEY_SECRET ?? config.apiKeySecret; + const walletSecret = process.env.CDP_WALLET_SECRET ?? config.walletSecret; + + if (apiKeyId && apiKeySecret && walletSecret) { + return { apiKeyId, apiKeySecret, walletSecret }; + } + + if (opts.interactive && process.stdin.isTTY) { + const prompted: CdpCredentials = { + apiKeyId: apiKeyId ?? await input({ message: 'CDP API Key ID:' }), + apiKeySecret: apiKeySecret ?? await promptPassword({ message: 'CDP API Key Secret:', mask: '*' }), + walletSecret: walletSecret ?? await promptPassword({ message: 'CDP Wallet Secret:', mask: '*' }), + }; + if (!prompted.apiKeyId.trim() || !prompted.apiKeySecret.trim() || !prompted.walletSecret.trim()) { + throw new Error('All three CDP credentials are required.'); + } + writeProviderConfig('cdp', { + apiKeyId: prompted.apiKeyId.trim(), + apiKeySecret: prompted.apiKeySecret.trim(), + walletSecret: prompted.walletSecret.trim(), + }); + console.log('CDP credentials saved to ~/.radius/config.json'); + return prompted; + } + + const missing = [ + !apiKeyId && 'CDP_API_KEY_ID', + !apiKeySecret && 'CDP_API_KEY_SECRET', + !walletSecret && 'CDP_WALLET_SECRET', + ].filter(Boolean); + + throw new Error( + `CDP credentials not configured (missing: ${missing.join(', ')}).\n` + + 'Run `radius-cli --wallet cdp wallet login` to set them up,\n' + + 'or set CDP_API_KEY_ID, CDP_API_KEY_SECRET, and CDP_WALLET_SECRET environment variables.\n' + + 'Get credentials from https://portal.cdp.coinbase.com', + ); +} + +async function makeCdpClient(creds: CdpCredentials) { + const { CdpClient } = await loadCdpSDK(); + return new CdpClient({ + apiKeyId: creds.apiKeyId, + apiKeySecret: creds.apiKeySecret, + walletSecret: creds.walletSecret, + }); +} + +async function getOrCreateCdpAccount(creds: CdpCredentials, session: CdpSession) { + const cdp = await makeCdpClient(creds); + try { + return await cdp.evm.getAccount({ address: session.address as Address }); + } catch (e) { + if (!session.accountName) throw e; + return await cdp.evm.getOrCreateAccount({ name: session.accountName }); + } +} + +async function listCdpAccounts(cdp: Awaited>): Promise { + const accounts: CdpSession[] = []; + let pageToken: string | undefined; + do { + const page = await cdp.evm.listAccounts({ pageSize: 25, pageToken }); + accounts.push(...page.accounts.map((account) => ({ + address: account.address, + accountName: account.name, + }))); + pageToken = page.nextPageToken; + } while (pageToken); + return accounts; +} + +async function chooseCdpSession(cdp: Awaited>): Promise { + let accounts: CdpSession[] = []; + try { + accounts = await listCdpAccounts(cdp); + } catch { + // Listing is a convenience. Manual get-or-create still covers the login path. + } + + if (accounts.length > 0 && process.stdin.isTTY) { + const choice = await select<{ type: 'existing'; session: CdpSession } | { type: 'new' } | { type: 'manual' }>({ + message: 'CDP account:', + choices: [ + ...accounts.map((session) => ({ + name: `${session.accountName ?? '(unnamed)'} ${session.address}`, + value: { type: 'existing' as const, session }, + })), + { name: 'Create a new account', value: { type: 'new' as const } }, + { name: 'Enter account name manually', value: { type: 'manual' as const } }, + ], + }); + + if (choice.type === 'existing') return choice.session; + if (choice.type === 'new') { + const name = (await input({ message: 'New account name (leave empty to auto-generate):' })).trim(); + const accountName = name || `radius-cli-${Date.now()}`; + const account = await cdp.evm.getOrCreateAccount({ name: accountName }); + return { address: account.address, accountName }; + } + } + + const userInput = await input({ + message: accounts.length > 0 + ? 'Account name:' + : 'Account name (leave empty to create new):', + }); + + const accountName = userInput.trim() || `radius-cli-${Date.now()}`; + const account = await cdp.evm.getOrCreateAccount({ name: accountName }); + return { address: account.address, accountName }; +} + +export const cdpProvider: WalletProvider = { + async login(_cfg: ResolvedConfig, opts?: { reset?: boolean }): Promise { + if (opts?.reset) { + deleteSession(); + deleteProviderConfig('cdp'); + console.log('CDP credentials and session cleared.'); + } + + const existing = readSession(); + if (existing) { + console.log(`Already logged in with CDP (${existing.address})`); + console.log('Run `radius-cli --wallet cdp wallet logout` first, or use `wallet login --reset` to start over.'); + return; + } + + const creds = await resolveCredentials({ interactive: true }); + const cdp = await makeCdpClient(creds); + const session = await chooseCdpSession(cdp); + writeSession(session); + + console.log(`CDP account ready`); + console.log(`Address: ${session.address}`); + if (session.accountName) console.log(`Account: ${session.accountName}`); + console.log(`Session saved to ${providerSessionPath(SESSION_FILE)}`); + }, + + async logout(_cfg: ResolvedConfig): Promise { + const session = readSession(); + if (!session) { + console.log('Not logged in to CDP.'); + return; + } + deleteSession(); + console.log(`Logged out of CDP (${session.address}).`); + }, + + async status(_cfg: ResolvedConfig, opts: GlobalOptions): Promise { + const session = readSession(); + if (opts.json) { + console.log(jsonStringify({ + provider: 'cdp', + loggedIn: !!session, + address: session?.address ?? null, + accountName: session?.accountName ?? null, + })); + return; + } + console.log('Provider: cdp'); + if (session) { + console.log(`Address: ${session.address}`); + if (session.accountName) console.log(`Account: ${session.accountName}`); + } else { + console.log('Status: not logged in'); + console.log('Run `radius-cli --wallet cdp wallet login` to set up CDP.'); + } + }, + + async getAccount(_cfg: ResolvedConfig): Promise { + const session = readSession(); + if (!session) { + throw new Error( + 'Not logged in to CDP. Run `radius-cli wallet login` first.', + ); + } + + const creds = await resolveCredentials(); + const cdpAccount = await getOrCreateCdpAccount(creds, session); + + // Wrap CDP account with toAccount() so viem recognizes it as type: "local". + // CDP's EvmServerAccount has type: "evm-server" which viem rejects. + // Delegate all signing to CDP's MPC infrastructure; the signed tx + // is then broadcast through the configured Radius RPC by viem's walletClient. + return toAccount({ + address: session.address as Address, + async sign({ hash }) { + return await cdpAccount.sign({ hash }) as Hex; + }, + async signMessage({ message }) { + return await cdpAccount.signMessage({ message }) as Hex; + }, + async signTransaction(tx) { + // CDP's signTransaction doesn't support legacy transactions (Radius uses legacy). + return signLegacyTransaction(tx, async (hash) => { + const signature = await cdpAccount.sign({ hash }); + return signature as Hex; + }); + }, + async signTypedData(typedData) { + return await cdpAccount.signTypedData(typedData) as Hex; + }, + }); + }, + + async getAddress(_cfg: ResolvedConfig): Promise
{ + const session = readSession(); + if (!session) { + throw new Error( + 'Not logged in to CDP. Run `radius-cli wallet login` first.', + ); + } + return session.address as Address; + }, + + // CDP manages keys server-side — no exportable private key via CLI. + // Omitting exportPrivateKey makes wallet.ts throw the appropriate error. +}; diff --git a/src/lib/providers/index.ts b/src/lib/providers/index.ts new file mode 100644 index 0000000..a398dfd --- /dev/null +++ b/src/lib/providers/index.ts @@ -0,0 +1,19 @@ +import { keystoreProvider } from './keystore.js'; +import { paraProvider } from './para.js'; +import { cdpProvider } from './cdp.js'; +import { privyProvider } from './privy.js'; +import { proxyProvider } from './proxy.js'; +import type { WalletProviderName } from '../../types.js'; +import type { WalletProvider } from './types.js'; + +const providers: Record = { + keystore: keystoreProvider, + cdp: cdpProvider, + para: paraProvider, + privy: privyProvider, + proxy: proxyProvider, +}; + +export function getProvider(name: WalletProviderName): WalletProvider { + return providers[name]; +} diff --git a/src/lib/providers/keystore.ts b/src/lib/providers/keystore.ts new file mode 100644 index 0000000..6236781 --- /dev/null +++ b/src/lib/providers/keystore.ts @@ -0,0 +1,84 @@ +import { password as promptPassword } from '@inquirer/prompts'; +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; +import type { Address, Hex, LocalAccount } from 'viem'; +import { keystoreExists, loadAccount, loadKeystorePrivateKey, readKeystoreAddress, saveKeystore } from '../keystore.js'; +import { readPasswordless, writeCachedAddress, writePasswordless } from '../config.js'; +import { jsonStringify } from '../format.js'; +import type { ResolvedConfig, GlobalOptions } from '../../types.js'; +import type { WalletProvider } from './types.js'; + +async function resolvePassword(cfg: ResolvedConfig): Promise { + if (cfg.password !== undefined) return cfg.password; + if (readPasswordless()) return ''; + return await promptPassword({ message: 'Keystore password:', mask: '*' }); +} + +async function autoCreateKeystore(cfg: ResolvedConfig): Promise { + const pk = generatePrivateKey(); + const password = cfg.password ?? ''; + const address = await saveKeystore(cfg.keystorePath, pk, password); + writeCachedAddress(address); + writePasswordless(password === ''); + + const lines = [ + `Created new keystore at ${cfg.keystorePath}`, + `Address: ${address}`, + ]; + if (password === '') { + lines.push( + 'No password set — keystore is effectively unencrypted (file mode 0o600).', + 'To rotate to a password-protected keystore: `radius-cli wallet new --force`', + ); + } + process.stderr.write(lines.join('\n') + '\n'); + + return privateKeyToAccount(pk); +} + +export const keystoreProvider: WalletProvider = { + async getAccount(cfg: ResolvedConfig): Promise { + if (!keystoreExists(cfg.keystorePath)) { + return await autoCreateKeystore(cfg); + } + const password = await resolvePassword(cfg); + return await loadAccount(cfg.keystorePath, password); + }, + + async getAddress(cfg: ResolvedConfig): Promise
{ + const addr = readKeystoreAddress(cfg.keystorePath); + if (addr) return addr; + return (await autoCreateKeystore(cfg)).address; + }, + + async login(_cfg: ResolvedConfig): Promise { + console.log( + 'Keystore wallets do not require login.\n' + + 'To create a new wallet: radius-cli wallet new\n' + + 'To import an existing key: radius-cli wallet import ', + ); + }, + + async logout(_cfg: ResolvedConfig): Promise { + // Keystore logout is a no-op. + }, + + async status(cfg: ResolvedConfig, opts: GlobalOptions): Promise { + const address = readKeystoreAddress(cfg.keystorePath); + if (opts.json) { + console.log(jsonStringify({ provider: 'keystore', address: address ?? null })); + return; + } + console.log(`Provider: keystore`); + if (address) { + console.log(`Address: ${address}`); + } else { + console.log('No keystore found. Run `radius-cli wallet new` to create one.'); + } + }, + + async exportPrivateKey(cfg: ResolvedConfig): Promise { + const password = cfg.password + ?? (readPasswordless() ? '' : await promptPassword({ message: 'Keystore password:', mask: '*' })); + return await loadKeystorePrivateKey(cfg.keystorePath, password); + }, +}; diff --git a/src/lib/providers/para.ts b/src/lib/providers/para.ts new file mode 100644 index 0000000..9ec527f --- /dev/null +++ b/src/lib/providers/para.ts @@ -0,0 +1,315 @@ +import { input, confirm } from '@inquirer/prompts'; +import type { Address, Hex, LocalAccount } from 'viem'; +import { readProviderConfig, writeProviderConfig, deleteProviderConfig } from '../config.js'; +import { jsonStringify } from '../format.js'; +import { disableProviderTelemetry } from '../providerTelemetry.js'; +import { + moveProviderSession, + providerSessionPath, + readProviderSession, + readProviderSessionFile, + writeProviderSession, +} from './session.js'; +import type { ResolvedConfig, GlobalOptions } from '../../types.js'; +import type { WalletProvider } from './types.js'; + +const SESSION_FILE = 'para-session.json'; +const BACKUP_SESSION_FILE = 'para-session.bak.json'; +const PARA_SERVER_OPTS = { disableWorkers: true, disableWebSockets: true } as const; + +interface ParaSession { + email: string; + userShare: string; + walletId: string; + address: string; +} + +function sessionPath(): string { + return providerSessionPath(SESSION_FILE); +} + +function readSessionFile(p: string): ParaSession | null { + return readProviderSessionFile(p); +} + +function readSession(): ParaSession | null { + return readProviderSession(SESSION_FILE); +} + +function writeSession(session: ParaSession): void { + writeProviderSession(SESSION_FILE, session); +} + +function backupSessionPath(): string { + return providerSessionPath(BACKUP_SESSION_FILE); +} + +function archiveSession(): string | null { + return moveProviderSession(SESSION_FILE, BACKUP_SESSION_FILE); +} + +async function restoreArchivedSession(): Promise { + const backupPath = backupSessionPath(); + const archived = readSessionFile(backupPath); + if (!archived) return false; + + const shouldRestore = process.stdin.isTTY + ? await confirm({ + message: `Restore archived Para session for ${archived.email} (${archived.address})?`, + default: true, + }) + : true; + + if (!shouldRestore) return false; + + if (!moveProviderSession(BACKUP_SESSION_FILE, SESSION_FILE)) return false; + console.log(`Restored Para session from ${backupPath}.`); + console.log(`Address: ${archived.address}`); + return true; +} + +function normalizeParaError(e: unknown): Error { + if (e instanceof Error) return e; + if (typeof e === 'object' && e !== null) { + const obj = e as Record; + const msg = typeof obj['message'] === 'string' ? obj['message'] : JSON.stringify(e, null, 2); + return new Error(`Para SDK error: ${msg}`); + } + return new Error(`Para SDK error: ${String(e)}`); +} + +async function loadParaSDK() { + disableProviderTelemetry('para'); + try { + const { Para, Environment } = await import('@getpara/server-sdk'); + const { createParaViemAccount } = await import('@getpara/viem-v2-integration'); + return { Para, Environment, createParaViemAccount }; + } catch { + throw new Error( + 'Para SDK packages are not installed. Run:\n' + + ' npm install @getpara/server-sdk @getpara/viem-v2-integration\n' + + 'to enable the Para wallet provider.', + ); + } +} + +async function resolveApiKey(opts: { interactive?: boolean } = {}): Promise { + // env var → config → prompt (if interactive) → error + const key = process.env.PARA_API_KEY ?? readProviderConfig('para').apiKey; + if (key) return key; + + if (opts.interactive && process.stdin.isTTY) { + const prompted = await input({ + message: 'Para API key (from https://developer.getpara.com):', + }); + if (!prompted.trim()) throw new Error('API key is required.'); + writeProviderConfig('para', { apiKey: prompted.trim() }); + console.log('API key saved to ~/.radius/config.json'); + return prompted.trim(); + } + + throw new Error( + 'Para API key not configured. Run `radius-cli --wallet para wallet login` to set it up,\n' + + 'or set the PARA_API_KEY environment variable.\n' + + 'Get your API key from https://developer.getpara.com', + ); +} + +function resolveEnvironmentValue(): string { + return (process.env.PARA_ENV ?? readProviderConfig('para').env ?? 'BETA').toUpperCase(); +} + +export const paraProvider: WalletProvider = { + async login(_cfg: ResolvedConfig, opts?: { reset?: boolean }): Promise { + if (opts?.reset) { + const backupPath = archiveSession(); + deleteProviderConfig('para'); + console.log('Para credentials and session cleared.'); + if (backupPath) console.log(`Previous Para session archived to ${backupPath}.`); + } + + const existing = readSession(); + if (existing) { + console.log(`Already logged in as ${existing.email} (${existing.address})`); + console.log('Run `radius-cli --wallet para wallet logout` first, or use `wallet login --reset` to start over.'); + return; + } + + if (!opts?.reset && await restoreArchivedSession()) { + return; + } + + const sdk = await loadParaSDK(); + const apiKey = await resolveApiKey({ interactive: true }); + const email = await input({ message: 'Para email:' }); + if (!email.trim()) throw new Error('Email is required.'); + + const envStr = resolveEnvironmentValue(); + const env = sdk.Environment[envStr as keyof typeof sdk.Environment] ?? sdk.Environment.BETA; + + let para: InstanceType; + try { + para = new sdk.Para(env, apiKey, PARA_SERVER_OPTS); + } catch (e) { + throw normalizeParaError(e); + } + + let hasWallet: boolean; + try { + hasWallet = await para.hasPregenWallet({ pregenId: { email } }); + } catch (e) { + throw normalizeParaError(e); + } + + let walletId: string; + let address: string; + + if (hasWallet) { + let wallets: Awaited>; + try { + wallets = await para.getPregenWallets({ pregenId: { email } }); + } catch (e) { + throw normalizeParaError(e); + } + const evmWallet = wallets.find((w) => w.type === 'EVM'); + if (!evmWallet) { + throw new Error('No EVM wallet found for this email. Create one at https://developer.getpara.com'); + } + walletId = evmWallet.id; + address = evmWallet.address ?? ''; + } else { + let wallet: Awaited>; + try { + wallet = await para.createPregenWallet({ + type: 'EVM', + pregenId: { email }, + }); + } catch (e) { + throw normalizeParaError(e); + } + walletId = wallet.id; + address = wallet.address ?? ''; + } + + if (!address) { + const wallets = para.getWallets(); + const w = wallets[walletId]; + address = w?.address ?? ''; + } + + // getUserShare() returns the in-memory MPC signer encoded as base64. + // It is only populated right after createPregenWallet() — getPregenWallets() + // returns wallet metadata only; Para's servers never hold the user share. + const userShare = para.getUserShare(); + if (!userShare) { + const backupPath = backupSessionPath(); + const hint = readSessionFile(backupPath) + ? `A previous session backup exists at ${backupPath}.\n` + + 'Run `radius-cli --wallet para wallet login` and choose restore.' + : 'No session backup found. The signing key for this address cannot be recovered.'; + throw new Error( + `Para wallet ${address} exists but the signing key is not available.\n\n` + + 'Para\'s MPC signing key (user share) is generated once at wallet creation and\n' + + 'is never stored by Para\'s servers. If you ran `wallet logout`, the key was\n' + + 'archived or deleted from this machine.\n\n' + + hint + '\n\n' + + 'To use a new wallet, register with a different email address.', + ); + } + + const session: ParaSession = { email, userShare, walletId, address }; + writeSession(session); + + // Release any SDK resources (timers, sockets) so the process exits cleanly. + if (typeof (para as any).disconnect === 'function') { + try { await (para as any).disconnect(); } catch { /* best-effort */ } + } + + console.log(`Logged in as ${email} (pregenerated wallet — not yet claimed)`); + console.log(`Address: ${address}`); + console.log(`Session saved to ${sessionPath()}`); + }, + + async logout(_cfg: ResolvedConfig): Promise { + const session = readSession(); + if (!session) { + console.log('Not logged in to Para.'); + return; + } + + console.log(`Address: ${session.address}`); + console.log(`\nThis signs out by archiving the local Para session.`); + console.log('The signing key is preserved and can be restored with `radius-cli --wallet para wallet login`.'); + console.log(`The session will be archived to ${backupSessionPath()}.\n`); + + const ok = process.stdin.isTTY + ? await confirm({ message: 'Continue with logout?', default: false }) + : true; + + if (!ok) { + console.log('Logout cancelled.'); + return; + } + + const backupPath = archiveSession(); + console.log(`Logged out of Para (${session.email}).`); + if (backupPath) console.log(`Session archived to ${backupPath}.`); + console.log('Run `radius-cli --wallet para wallet login` to restore it later.'); + }, + + async status(_cfg: ResolvedConfig, opts: GlobalOptions): Promise { + const session = readSession(); + if (opts.json) { + console.log(jsonStringify({ + provider: 'para', + loggedIn: !!session, + email: session?.email ?? null, + address: session?.address ?? null, + })); + return; + } + console.log('Provider: para'); + if (session) { + console.log(`Email: ${session.email}`); + console.log(`Address: ${session.address}`); + } else { + console.log('Status: not logged in'); + console.log('Run `radius-cli --wallet para wallet login` to authenticate with Para.'); + } + }, + + async getAccount(_cfg: ResolvedConfig): Promise { + const session = readSession(); + if (!session) { + throw new Error( + 'Not logged in to Para. Run `radius-cli wallet login` first.', + ); + } + + const sdk = await loadParaSDK(); + const apiKey = await resolveApiKey(); + const envStr = resolveEnvironmentValue(); + const env = sdk.Environment[envStr as keyof typeof sdk.Environment] ?? sdk.Environment.BETA; + const para = new sdk.Para(env, apiKey, PARA_SERVER_OPTS); + await para.setUserShare(session.userShare); + await para.setCurrentWalletIds({ EVM: [session.walletId] }); + + // Pass session address to ensure we sign as the wallet that login printed. + // Cast needed: server SDK's Para extends ParaCore but has a slightly + // different claimPregenWallets return type. Functionally identical for signing. + return sdk.createParaViemAccount({ para: para as any, address: session.address as Hex }); + }, + + async getAddress(_cfg: ResolvedConfig): Promise
{ + const session = readSession(); + if (!session) { + throw new Error( + 'Not logged in to Para. Run `radius-cli wallet login` first.', + ); + } + return session.address as Address; + }, + + // Para uses MPC — no single private key exists. Export is not possible. + // Omitting exportPrivateKey makes wallet.ts throw the appropriate error. +}; diff --git a/src/lib/providers/privy.ts b/src/lib/providers/privy.ts new file mode 100644 index 0000000..105c29f --- /dev/null +++ b/src/lib/providers/privy.ts @@ -0,0 +1,346 @@ +import { input, password as promptPassword, select } from '@inquirer/prompts'; +import { + type Address, + type Hex, + type LocalAccount, + toHex, +} from 'viem'; +import { toAccount } from 'viem/accounts'; +import { readProviderConfig, writeProviderConfig, deleteProviderConfig } from '../config.js'; +import { jsonStringify } from '../format.js'; +import { disableProviderTelemetry } from '../providerTelemetry.js'; +import { signLegacyTransaction } from './remoteSigning.js'; +import { deleteProviderSession, providerSessionPath, readProviderSession, writeProviderSession } from './session.js'; +import type { ResolvedConfig, GlobalOptions } from '../../types.js'; +import type { WalletProvider } from './types.js'; + +const PRIVY_API_BASE = 'https://api.privy.io/v1'; +const SESSION_FILE = 'privy-session.json'; + +interface PrivySession { + walletId: string; + address: string; +} + +interface PrivyCredentials { + appId: string; + appSecret: string; +} + +interface PrivyWallet { + id: string; + address: string; +} + +function readSession(): PrivySession | null { + return readProviderSession(SESSION_FILE); +} + +function writeSession(session: PrivySession): void { + writeProviderSession(SESSION_FILE, session); +} + +function deleteSession(): void { + deleteProviderSession(SESSION_FILE); +} + +async function resolveCredentials(opts: { interactive?: boolean } = {}): Promise { + disableProviderTelemetry('privy'); + const config = readProviderConfig('privy'); + const appId = process.env.PRIVY_APP_ID ?? config.appId; + const appSecret = process.env.PRIVY_APP_SECRET ?? config.appSecret; + + if (appId && appSecret) return { appId, appSecret }; + + if (opts.interactive && process.stdin.isTTY) { + const prompted: PrivyCredentials = { + appId: appId ?? await input({ message: 'Privy App ID:' }), + appSecret: appSecret ?? await promptPassword({ message: 'Privy App Secret:', mask: '*' }), + }; + if (!prompted.appId.trim() || !prompted.appSecret.trim()) { + throw new Error('Both Privy App ID and App Secret are required.'); + } + writeProviderConfig('privy', { + appId: prompted.appId.trim(), + appSecret: prompted.appSecret.trim(), + }); + console.log('Privy credentials saved to ~/.radius/config.json'); + return prompted; + } + + const missing = [ + !appId && 'PRIVY_APP_ID', + !appSecret && 'PRIVY_APP_SECRET', + ].filter(Boolean); + + throw new Error( + `Privy credentials not configured (missing: ${missing.join(', ')}).\n` + + 'Run `radius-cli --wallet privy wallet login` to set them up,\n' + + 'or set PRIVY_APP_ID and PRIVY_APP_SECRET environment variables.\n' + + 'Get credentials from https://dashboard.privy.io', + ); +} + +function authHeaders(creds: PrivyCredentials): Record { + const basic = Buffer.from(`${creds.appId}:${creds.appSecret}`).toString('base64'); + return { + 'authorization': `Basic ${basic}`, + 'privy-app-id': creds.appId, + 'content-type': 'application/json', + }; +} + +function parsePrivyWallets(json: unknown): PrivyWallet[] { + const obj = json as { data?: unknown; wallets?: unknown; items?: unknown }; + const maybeWallets = Array.isArray(json) + ? json + : Array.isArray(obj.data) + ? obj.data + : Array.isArray(obj.wallets) + ? obj.wallets + : Array.isArray(obj.items) + ? obj.items + : []; + + return maybeWallets.flatMap((wallet) => { + const candidate = wallet as { id?: unknown; address?: unknown }; + return typeof candidate.id === 'string' && typeof candidate.address === 'string' + ? [{ id: candidate.id, address: candidate.address }] + : []; + }); +} + +async function listPrivyWallets(creds: PrivyCredentials): Promise { + const res = await fetch(`${PRIVY_API_BASE}/wallets`, { + method: 'GET', + headers: authHeaders(creds), + }); + if (!res.ok) return []; + return parsePrivyWallets(await res.json()); +} + +async function fetchPrivyWallet(creds: PrivyCredentials, walletId: string): Promise { + const res = await fetch(`${PRIVY_API_BASE}/wallets/${walletId}`, { + method: 'GET', + headers: authHeaders(creds), + }); + if (!res.ok) { + const text = await res.text().catch(() => ''); + throw new Error(`Failed to fetch wallet ${walletId}: ${res.status} ${text}`); + } + return await res.json() as PrivyWallet; +} + +async function createPrivyWallet(creds: PrivyCredentials): Promise { + const res = await fetch(`${PRIVY_API_BASE}/wallets`, { + method: 'POST', + headers: authHeaders(creds), + body: JSON.stringify({ chain_type: 'ethereum' }), + }); + if (!res.ok) { + const text = await res.text().catch(() => ''); + throw new Error(`Failed to create wallet: ${res.status} ${text}`); + } + return await res.json() as PrivyWallet; +} + +async function choosePrivySession(creds: PrivyCredentials): Promise { + const wallets = await listPrivyWallets(creds).catch(() => []); + + if (wallets.length > 0 && process.stdin.isTTY) { + const choice = await select<{ type: 'existing'; wallet: PrivyWallet } | { type: 'new' } | { type: 'manual' }>({ + message: 'Privy wallet:', + choices: [ + ...wallets.map((wallet) => ({ + name: `${wallet.address} (${wallet.id})`, + value: { type: 'existing' as const, wallet }, + })), + { name: 'Create a new wallet', value: { type: 'new' as const } }, + { name: 'Enter wallet ID manually', value: { type: 'manual' as const } }, + ], + }); + + if (choice.type === 'existing') { + return { walletId: choice.wallet.id, address: choice.wallet.address }; + } + if (choice.type === 'new') { + const wallet = await createPrivyWallet(creds); + console.log(`Created new Privy wallet`); + return { walletId: wallet.id, address: wallet.address }; + } + } + + const walletId = await input({ + message: 'Privy wallet ID (leave empty to create new):', + }); + + if (walletId.trim()) { + const wallet = await fetchPrivyWallet(creds, walletId.trim()); + return { walletId: wallet.id, address: wallet.address }; + } + + const wallet = await createPrivyWallet(creds); + console.log(`Created new Privy wallet`); + return { walletId: wallet.id, address: wallet.address }; +} + +async function privyRpc( + creds: PrivyCredentials, + walletId: string, + method: string, + params: Record, + caip2?: string, +): Promise { + const body: Record = { method, params }; + if (caip2) body.caip2 = caip2; + + const res = await fetch(`${PRIVY_API_BASE}/wallets/${walletId}/rpc`, { + method: 'POST', + headers: authHeaders(creds), + body: JSON.stringify(body, (_, v) => (typeof v === 'bigint' ? v.toString() : v)), + }); + + if (!res.ok) { + const text = await res.text().catch(() => ''); + let detail = ''; + try { + const err = JSON.parse(text) as { error?: { message?: string } }; + detail = err.error?.message ?? text; + } catch { + detail = text; + } + throw new Error(`Privy RPC ${method} failed (${res.status}): ${detail}`); + } + + const json = await res.json() as { data?: unknown; method?: string }; + return json.data; +} + +function buildPrivyAccount(session: PrivySession, creds: PrivyCredentials): LocalAccount { + return toAccount({ + address: session.address as Address, + + async sign({ hash }) { + const data = await privyRpc(creds, session.walletId, 'secp256k1_sign', { + hash, + }); + return (data as { signature: string }).signature as Hex; + }, + + async signMessage({ message }) { + let hexMessage: string; + if (typeof message === 'string') { + hexMessage = toHex(new TextEncoder().encode(message)); + } else if (message.raw instanceof Uint8Array) { + hexMessage = toHex(message.raw); + } else { + hexMessage = message.raw; + } + + const data = await privyRpc(creds, session.walletId, 'personal_sign', { + message: hexMessage, + encoding: 'hex', + }); + return (data as { signature: string }).signature as Hex; + }, + + async signTransaction(tx) { + return signLegacyTransaction(tx, async (hash) => { + const data = await privyRpc(creds, session.walletId, 'secp256k1_sign', { hash }); + return (data as { signature: string }).signature as Hex; + }); + }, + + async signTypedData(typedData) { + const data = await privyRpc(creds, session.walletId, 'eth_signTypedData_v4', { + typed_data: { + domain: typedData.domain, + types: typedData.types, + primary_type: typedData.primaryType, + message: typedData.message, + }, + }); + return (data as { signature: string }).signature as Hex; + }, + }); +} + +export const privyProvider: WalletProvider = { + async login(_cfg: ResolvedConfig, opts?: { reset?: boolean }): Promise { + if (opts?.reset) { + deleteSession(); + deleteProviderConfig('privy'); + console.log('Privy credentials and session cleared.'); + } + + const existing = readSession(); + if (existing) { + console.log(`Already logged in with Privy (${existing.address})`); + console.log('Run `radius-cli --wallet privy wallet logout` first to switch wallets.'); + return; + } + + const creds = await resolveCredentials({ interactive: true }); + const session = await choosePrivySession(creds); + + writeSession(session); + console.log(`Address: ${session.address}`); + console.log(`Wallet ID: ${session.walletId}`); + console.log(`Session saved to ${providerSessionPath(SESSION_FILE)}`); + }, + + async logout(_cfg: ResolvedConfig): Promise { + const session = readSession(); + if (!session) { + console.log('Not logged in to Privy.'); + return; + } + deleteSession(); + console.log(`Logged out of Privy (${session.address}).`); + }, + + async status(_cfg: ResolvedConfig, opts: GlobalOptions): Promise { + const session = readSession(); + if (opts.json) { + console.log(jsonStringify({ + provider: 'privy', + loggedIn: !!session, + address: session?.address ?? null, + walletId: session?.walletId ?? null, + })); + return; + } + console.log('Provider: privy'); + if (session) { + console.log(`Address: ${session.address}`); + console.log(`Wallet ID: ${session.walletId}`); + } else { + console.log('Status: not logged in'); + console.log('Run `radius-cli --wallet privy wallet login` to set up Privy.'); + } + }, + + async getAccount(_cfg: ResolvedConfig): Promise { + const session = readSession(); + if (!session) { + throw new Error( + 'Not logged in to Privy. Run `radius-cli --wallet privy wallet login` first.', + ); + } + const creds = await resolveCredentials(); + return buildPrivyAccount(session, creds); + }, + + async getAddress(_cfg: ResolvedConfig): Promise
{ + const session = readSession(); + if (!session) { + throw new Error( + 'Not logged in to Privy. Run `radius-cli --wallet privy wallet login` first.', + ); + } + return session.address as Address; + }, + + // Privy uses server-side MPC — no exportable private key. + // Omitting exportPrivateKey makes wallet.ts throw the appropriate error. +}; diff --git a/src/lib/providers/proxy.ts b/src/lib/providers/proxy.ts new file mode 100644 index 0000000..4b23659 --- /dev/null +++ b/src/lib/providers/proxy.ts @@ -0,0 +1,289 @@ +import { + type Address, + type Hex, + type LocalAccount, + isAddress, + isHex, + toHex, +} from 'viem'; +import { toAccount } from 'viem/accounts'; +import { readProviderConfig } from '../config.js'; +import { jsonStringify } from '../format.js'; +import { signLegacyTransaction } from './remoteSigning.js'; +import type { ResolvedConfig, GlobalOptions } from '../../types.js'; +import type { WalletProvider } from './types.js'; + +interface ProxyConfig { + url: string; + alias: string; + token?: string; + accessClientId?: string; + accessClientSecret?: string; +} + +interface PartialProxyConfig { + url?: string; + alias?: string; + token?: string; + accessClientId?: string; + accessClientSecret?: string; +} + +interface ProxyAddressResponse { + provider?: string; + alias?: string; + address?: string; +} + +interface ProxyStatusResponse { + provider?: string; + alias?: string; + status?: string; + address?: string | null; +} + +interface ProxyCapabilitiesResponse { + provider?: string; + alias?: string; + capabilities?: Record; +} + +interface ProxySignatureResponse { + provider?: string; + alias?: string; + signature?: string; +} + +function readProxyConfig(): PartialProxyConfig { + const config = readProviderConfig('proxy'); + return { + url: process.env.RADIUS_WALLET_PROXY_URL ?? config.url, + alias: process.env.RADIUS_WALLET_ALIAS ?? config.alias, + token: process.env.RADIUS_WALLET_PROXY_TOKEN ?? config.token, + accessClientId: process.env.CF_ACCESS_CLIENT_ID, + accessClientSecret: process.env.CF_ACCESS_CLIENT_SECRET, + }; +} + +function requireProxyConfig(): ProxyConfig { + const config = readProxyConfig(); + const missing = [ + !config.url && 'RADIUS_WALLET_PROXY_URL', + !config.alias && 'RADIUS_WALLET_ALIAS', + ].filter(Boolean); + + if (missing.length > 0) { + throw new Error( + `Proxy wallet not configured (missing: ${missing.join(', ')}).\n` + + 'Set RADIUS_WALLET_PROXY_URL and RADIUS_WALLET_ALIAS,\n' + + 'or configure providers.proxy.url and providers.proxy.alias in ~/.radius/config.json.', + ); + } + + if (Boolean(config.accessClientId) !== Boolean(config.accessClientSecret)) { + throw new Error( + 'Cloudflare Access service token is incomplete. Set both CF_ACCESS_CLIENT_ID and CF_ACCESS_CLIENT_SECRET.', + ); + } + + return { + url: config.url!.replace(/\/+$/, ''), + alias: config.alias!, + token: config.token, + accessClientId: config.accessClientId, + accessClientSecret: config.accessClientSecret, + }; +} + +function walletUrl(config: ProxyConfig, operation: string): string { + return `${config.url}/v1/wallets/${encodeURIComponent(config.alias)}/${operation}`; +} + +function requestHeaders(config: ProxyConfig, hasBody: boolean): Record { + const headers: Record = { accept: 'application/json' }; + if (hasBody) headers['content-type'] = 'application/json'; + if (config.token) headers.authorization = `Bearer ${config.token}`; + if (config.accessClientId) headers['CF-Access-Client-Id'] = config.accessClientId; + if (config.accessClientSecret) headers['CF-Access-Client-Secret'] = config.accessClientSecret; + return headers; +} + +function jsonReplacer(_key: string, value: unknown): unknown { + return typeof value === 'bigint' ? value.toString() : value; +} + +async function proxyRequest( + config: ProxyConfig, + operation: string, + init: { method: 'GET' | 'POST'; body?: unknown }, +): Promise { + const hasBody = init.body !== undefined; + const res = await fetch(walletUrl(config, operation), { + method: init.method, + headers: requestHeaders(config, hasBody), + body: hasBody ? JSON.stringify(init.body, jsonReplacer) : undefined, + }); + + if (!res.ok) { + const detail = await parseProxyError(res); + throw new Error(`Proxy wallet ${operation} failed (${res.status}): ${detail}`); + } + + return await res.json() as T; +} + +async function parseProxyError(res: Response): Promise { + const text = await res.text().catch(() => ''); + if (!text) return res.statusText || 'request failed'; + try { + const parsed = JSON.parse(text) as { error?: { code?: string; message?: string } }; + if (parsed.error?.message) { + return parsed.error.code + ? `${parsed.error.code}: ${parsed.error.message}` + : parsed.error.message; + } + } catch { + // Fall through to raw response text. + } + return text; +} + +async function fetchAddress(config: ProxyConfig): Promise
{ + const data = await proxyRequest(config, 'address', { method: 'GET' }); + if (!data.address || !isAddress(data.address)) { + throw new Error('Proxy wallet address response did not include a valid 0x address.'); + } + return data.address as Address; +} + +function normalizeMessage(message: string | { raw: Hex | Uint8Array }): string { + if (typeof message === 'string') return message; + if (message.raw instanceof Uint8Array) return toHex(message.raw); + return message.raw; +} + +function requireSignature(data: ProxySignatureResponse, operation: string): Hex { + if (!data.signature || !isHex(data.signature)) { + throw new Error(`Proxy wallet ${operation} response did not include a valid signature.`); + } + return data.signature as Hex; +} + +async function signHash(config: ProxyConfig, hash: Hex): Promise { + const data = await proxyRequest(config, 'sign-transaction', { + method: 'POST', + body: { hash }, + }); + return requireSignature(data, 'sign-transaction'); +} + +function buildProxyAccount(config: ProxyConfig, address: Address): LocalAccount { + return toAccount({ + address, + + async sign({ hash }) { + return signHash(config, hash); + }, + + async signMessage({ message }) { + const data = await proxyRequest(config, 'sign-message', { + method: 'POST', + body: { message: normalizeMessage(message) }, + }); + return requireSignature(data, 'sign-message'); + }, + + async signTransaction(tx) { + return signLegacyTransaction(tx, (hash) => signHash(config, hash)); + }, + + async signTypedData(typedData) { + const data = await proxyRequest(config, 'sign-typed-data', { + method: 'POST', + body: { typedData }, + }); + return requireSignature(data, 'sign-typed-data'); + }, + }); +} + +function statusUrl(config: PartialProxyConfig): string | null { + return config.url ? config.url.replace(/\/+$/, '') : null; +} + +export const proxyProvider: WalletProvider = { + async status(_cfg: ResolvedConfig, opts: GlobalOptions): Promise { + const rawConfig = readProxyConfig(); + const configured = Boolean(rawConfig.url && rawConfig.alias); + let loggedIn = false; + let remoteProvider: string | null = null; + let remoteStatus: string | null = null; + let address: string | null = null; + let capabilities: Record | null = null; + let error: string | null = null; + + if (configured) { + try { + const config = requireProxyConfig(); + const status = await proxyRequest(config, 'status', { method: 'GET' }); + loggedIn = true; + remoteProvider = status.provider ?? null; + remoteStatus = status.status ?? null; + address = status.address && isAddress(status.address) ? status.address : null; + + try { + const caps = await proxyRequest(config, 'capabilities', { method: 'GET' }); + capabilities = caps.capabilities ?? null; + remoteProvider = remoteProvider ?? caps.provider ?? null; + } catch { + capabilities = null; + } + } catch (e) { + error = e instanceof Error ? e.message : String(e); + } + } + + if (opts.json) { + console.log(jsonStringify({ + provider: 'proxy', + configured, + loggedIn, + alias: rawConfig.alias ?? null, + url: statusUrl(rawConfig), + remoteProvider, + remoteStatus, + address, + capabilities, + error, + })); + return; + } + + console.log('Provider: proxy'); + console.log(`Status: ${configured ? (loggedIn ? 'configured' : 'unreachable') : 'not configured'}`); + if (rawConfig.alias) console.log(`Alias: ${rawConfig.alias}`); + if (rawConfig.url) console.log(`URL: ${statusUrl(rawConfig)}`); + if (remoteProvider) console.log(`Remote: ${remoteProvider}`); + if (remoteStatus) console.log(`Remote status: ${remoteStatus}`); + if (address) console.log(`Address: ${address}`); + if (capabilities) { + console.log(`Capabilities: signMessage=${!!capabilities.signMessage}, signTypedData=${!!capabilities.signTypedData}, signTransaction=${!!capabilities.signTransaction}`); + } + if (error) console.log(`Error: ${error}`); + if (!configured) { + console.log('Set RADIUS_WALLET_PROXY_URL and RADIUS_WALLET_ALIAS to use the proxy provider.'); + } + }, + + async getAccount(_cfg: ResolvedConfig): Promise { + const config = requireProxyConfig(); + const address = await fetchAddress(config); + return buildProxyAccount(config, address); + }, + + async getAddress(_cfg: ResolvedConfig): Promise
{ + return fetchAddress(requireProxyConfig()); + }, + + // The proxy worker is a remote secret boundary. Private key export is intentionally unsupported. +}; diff --git a/src/lib/providers/remoteSigning.ts b/src/lib/providers/remoteSigning.ts new file mode 100644 index 0000000..4b79495 --- /dev/null +++ b/src/lib/providers/remoteSigning.ts @@ -0,0 +1,11 @@ +import { keccak256, parseSignature, serializeTransaction, type Hex, type TransactionSerializable } from 'viem'; + +export async function signLegacyTransaction( + tx: unknown, + signHash: (hash: Hex) => Promise, +): Promise { + const transaction = tx as TransactionSerializable; + const serialized = serializeTransaction(transaction); + const signature = await signHash(keccak256(serialized)); + return serializeTransaction(transaction, parseSignature(signature)) as Hex; +} diff --git a/src/lib/providers/session.ts b/src/lib/providers/session.ts new file mode 100644 index 0000000..a904224 --- /dev/null +++ b/src/lib/providers/session.ts @@ -0,0 +1,39 @@ +import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { radiusDir } from '../config.js'; + +export function providerSessionPath(fileName: string): string { + return join(radiusDir(), fileName); +} + +export function readProviderSessionFile(path: string): T | null { + if (!existsSync(path)) return null; + try { + return JSON.parse(readFileSync(path, 'utf8')) as T; + } catch { + return null; + } +} + +export function readProviderSession(fileName: string): T | null { + return readProviderSessionFile(providerSessionPath(fileName)); +} + +export function writeProviderSession(fileName: string, session: T): void { + const dir = radiusDir(); + if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 }); + writeFileSync(providerSessionPath(fileName), JSON.stringify(session, null, 2), { mode: 0o600 }); +} + +export function deleteProviderSession(fileName: string): void { + const path = providerSessionPath(fileName); + if (existsSync(path)) unlinkSync(path); +} + +export function moveProviderSession(fromFileName: string, toFileName: string): string | null { + const fromPath = providerSessionPath(fromFileName); + if (!existsSync(fromPath)) return null; + const toPath = providerSessionPath(toFileName); + renameSync(fromPath, toPath); + return toPath; +} diff --git a/src/lib/providers/types.ts b/src/lib/providers/types.ts new file mode 100644 index 0000000..ecc124d --- /dev/null +++ b/src/lib/providers/types.ts @@ -0,0 +1,11 @@ +import type { Address, Hex, LocalAccount } from 'viem'; +import type { ResolvedConfig, GlobalOptions } from '../../types.js'; + +export interface WalletProvider { + getAccount(cfg: ResolvedConfig): Promise; + getAddress(cfg: ResolvedConfig): Promise
; + login?(cfg: ResolvedConfig, opts?: { reset?: boolean }): Promise; + logout?(cfg: ResolvedConfig): Promise; + status(cfg: ResolvedConfig, opts: GlobalOptions): Promise; + exportPrivateKey?(cfg: ResolvedConfig): Promise; +} diff --git a/src/lib/x402/eip2612.ts b/src/lib/x402/eip2612.ts new file mode 100644 index 0000000..2b2d32f --- /dev/null +++ b/src/lib/x402/eip2612.ts @@ -0,0 +1,113 @@ +import { parseSignature, type Address, type Hex, type LocalAccount, type PublicClient } from 'viem'; + +/** + * EIP-2612 permit support for x402 settlement on Radius. + * + * SBC (the Radius-native stablecoin) does not implement EIP-3009 + * transferWithAuthorization, so Radius x402 servers settle via EIP-2612: + * the client signs a `Permit(owner, spender, value, nonce, deadline)` for + * the server's settlement spender, and the facilitator calls `permit()` + * followed by `transferFrom()`. Servers advertise this with + * `extra.settlementMethod: "permit-transferFrom"` and + * `extra.settlementSpender` in the 402 challenge. + */ + +export const EIP2612_NONCES_ABI = [ + { + type: 'function', + name: 'nonces', + stateMutability: 'view', + inputs: [{ name: 'owner', type: 'address' }], + outputs: [{ name: '', type: 'uint256' }], + }, +] as const; + +export const EIP2612_TYPES = { + Permit: [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], +} as const; + +// type alias (not interface) so it satisfies the encoder's index signature +export type PermitPayload = { + kind: 'permit-eip2612'; + owner: Address; + spender: Address; + value: string; + nonce: string; + deadline: string; + v: number; + r: Hex; + s: Hex; +}; + +export async function readPermitNonce( + client: PublicClient, + asset: Address, + owner: Address, +): Promise { + return await client.readContract({ + address: asset, + abi: EIP2612_NONCES_ABI, + functionName: 'nonces', + args: [owner], + }); +} + +export interface Eip2612SignArgs { + asset: Address; + chainId: number; + name: string; + version: string; + spender: Address; + value: bigint; + nonce: bigint; + deadline: bigint; +} + +/** Raw EIP-2612 Permit signature — used directly by the permit2 gas-sponsoring extension. */ +export async function signEip2612Permit( + account: LocalAccount, + args: Eip2612SignArgs, +): Promise { + return await account.signTypedData({ + domain: { + name: args.name, + version: args.version, + chainId: args.chainId, + verifyingContract: args.asset, + }, + types: EIP2612_TYPES, + primaryType: 'Permit', + message: { + owner: account.address, + spender: args.spender, + value: args.value, + nonce: args.nonce, + deadline: args.deadline, + }, + }); +} + +export async function signPermit( + account: LocalAccount, + args: Eip2612SignArgs, +): Promise { + const signature = await signEip2612Permit(account, args); + const { v, r, s } = parseSignature(signature); + return { + kind: 'permit-eip2612', + owner: account.address, + spender: args.spender, + value: args.value.toString(), + nonce: args.nonce.toString(), + deadline: args.deadline.toString(), + v: Number(v), + r, + s, + }; +} diff --git a/src/lib/x402/eip3009.ts b/src/lib/x402/eip3009.ts index e9934d0..70d8b2e 100644 --- a/src/lib/x402/eip3009.ts +++ b/src/lib/x402/eip3009.ts @@ -1,5 +1,5 @@ import { randomBytes } from 'node:crypto'; -import type { Address, Hex, PrivateKeyAccount, PublicClient } from 'viem'; +import type { Address, Hex, LocalAccount, PublicClient } from 'viem'; import type { Authorization } from './protocol.js'; export const ERC20_X402_ABI = [ @@ -117,7 +117,7 @@ export function makeAuthorization(args: { } export async function signTransferAuthorization( - account: PrivateKeyAccount, + account: LocalAccount, args: { asset: Address; chainId: number; diff --git a/src/lib/x402/http.ts b/src/lib/x402/http.ts index 4dc8080..6e756dd 100644 --- a/src/lib/x402/http.ts +++ b/src/lib/x402/http.ts @@ -3,7 +3,7 @@ import { readFileSync } from 'node:fs'; export const SUPPORTED_VERBS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'] as const; export type HttpVerb = typeof SUPPORTED_VERBS[number]; -const FORBIDDEN_REQUEST_HEADERS = new Set(['host', 'x-payment']); +const FORBIDDEN_REQUEST_HEADERS = new Set(['host', 'x-payment', 'payment-signature']); const MAX_RESPONSE_BYTES = 25 * 1024 * 1024; export interface HttpResponse { diff --git a/src/lib/x402/permit2.ts b/src/lib/x402/permit2.ts new file mode 100644 index 0000000..af953c6 --- /dev/null +++ b/src/lib/x402/permit2.ts @@ -0,0 +1,128 @@ +import { randomBytes } from 'node:crypto'; +import type { Address, Hex, LocalAccount } from 'viem'; + +/** + * Official Radius x402 v2 settlement: Permit2 PermitWitnessTransferFrom. + * + * Servers advertise it with `extra.assetTransferMethod: "permit2"`. The client + * signs two EIP-712 messages sharing one deadline: + * + * 1. EIP-2612 Permit — spender is the canonical Permit2 contract, nonce is + * sequential from `token.nonces(owner)`. Goes in + * `extensions.eip2612GasSponsoring.info.signature` so the facilitator can + * establish the Permit2 allowance gas-free on the payer's behalf. + * 2. Permit2 PermitWitnessTransferFrom — spender is the x402 proxy, nonce is + * random, the witness binds the transfer to the merchant's payTo. Goes in + * `payload.signature`. + * + * Source of truth: radiustechsystems/skills plugins/radius/skills/x402 + * (scripts/x402-pay.mjs, references/permit2-typed-data.template.json). + */ + +export const PERMIT2_ADDRESS: Address = '0x000000000022D473030F116dDEE9F6B43aC78BA3'; +export const X402_PERMIT2_PROXY: Address = '0x402085c248EeA27D92E8b30b2C58ed07f9E20001'; + +export const PERMIT2_WITNESS_TYPES = { + PermitWitnessTransferFrom: [ + { name: 'permitted', type: 'TokenPermissions' }, + { name: 'spender', type: 'address' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + { name: 'witness', type: 'Witness' }, + ], + TokenPermissions: [ + { name: 'token', type: 'address' }, + { name: 'amount', type: 'uint256' }, + ], + Witness: [ + { name: 'to', type: 'address' }, + { name: 'validAfter', type: 'uint256' }, + ], +} as const; + +export function randomPermit2Nonce(): bigint { + return BigInt('0x' + randomBytes(16).toString('hex')); +} + +export async function signPermit2WitnessTransfer( + account: LocalAccount, + args: { + token: Address; + chainId: number; + amount: bigint; + payTo: Address; + nonce: bigint; + deadline: bigint; + }, +): Promise { + return await account.signTypedData({ + domain: { + name: 'Permit2', + chainId: args.chainId, + verifyingContract: PERMIT2_ADDRESS, + }, + types: PERMIT2_WITNESS_TYPES, + primaryType: 'PermitWitnessTransferFrom', + message: { + permitted: { token: args.token, amount: args.amount }, + spender: X402_PERMIT2_PROXY, + nonce: args.nonce, + deadline: args.deadline, + witness: { to: args.payTo, validAfter: 0n }, + }, + }); +} + +/** Assemble the full base64 PAYMENT-SIGNATURE payload per the radius x402 skill. */ +export function buildPermit2PaymentPayload(args: { + chainId: number; + resource: { url: string; description?: string; mimeType?: string }; + accepted: Record; + token: Address; + amount: bigint; + owner: Address; + payTo: Address; + permit2Signature: Hex; + permit2Nonce: bigint; + eip2612Signature: Hex; + eip2612Nonce: bigint; + deadline: bigint; +}): string { + const payload = { + x402Version: 2, + scheme: 'exact', + network: `eip155:${args.chainId}`, + resource: { + url: args.resource.url, + description: args.resource.description ?? '', + mimeType: args.resource.mimeType ?? 'application/json', + }, + accepted: args.accepted, + payload: { + signature: args.permit2Signature, + permit2Authorization: { + permitted: { token: args.token, amount: args.amount.toString() }, + from: args.owner, + spender: X402_PERMIT2_PROXY, + nonce: args.permit2Nonce.toString(), + deadline: args.deadline.toString(), + witness: { to: args.payTo, validAfter: '0' }, + }, + }, + extensions: { + eip2612GasSponsoring: { + info: { + from: args.owner, + asset: args.token, + spender: PERMIT2_ADDRESS, + amount: args.amount.toString(), + nonce: args.eip2612Nonce.toString(), + deadline: args.deadline.toString(), + signature: args.eip2612Signature, + version: '1', + }, + }, + }, + }; + return Buffer.from(JSON.stringify(payload), 'utf8').toString('base64'); +} diff --git a/src/lib/x402/protocol.ts b/src/lib/x402/protocol.ts index b3f6dbb..8cb51a4 100644 --- a/src/lib/x402/protocol.ts +++ b/src/lib/x402/protocol.ts @@ -14,11 +14,15 @@ export interface AcceptEntry { outputSchema?: unknown; maxTimeoutSeconds?: number; extra?: { name?: string; version?: string; [k: string]: unknown }; + /** the accepts entry exactly as the server sent it — echoed back as `accepted` in v2 payment headers */ + raw: Record; } export interface Challenge { x402Version: number; accepts: AcceptEntry[]; + /** v2 challenges carry a top-level resource object — echoed back in v2 payment headers */ + resource?: unknown; error?: string; } @@ -35,6 +39,9 @@ export interface PaymentPayload { x402Version: number; scheme: string; network: string; + /** v2 only: the chosen accepts entry verbatim and the challenge's resource object */ + accepted?: Record; + resource?: unknown; payload: { signature: Hex; authorization: Authorization }; } @@ -89,7 +96,14 @@ export function parseChallenge(raw: unknown): Challenge { network: asString(e.network, `accepts[${i}].network`), asset: asAddress(e.asset, `accepts[${i}].asset`), payTo: asAddress(e.payTo, `accepts[${i}].payTo`), - maxAmountRequired: asBigInt(e.maxAmountRequired, `accepts[${i}].maxAmountRequired`), + maxAmountRequired: asBigInt( + e.maxAmountRequired ?? e.amount, + e.maxAmountRequired !== undefined + ? `accepts[${i}].maxAmountRequired` + : e.amount !== undefined + ? `accepts[${i}].amount` + : `accepts[${i}].maxAmountRequired`, + ), resource: typeof e.resource === 'string' ? e.resource : undefined, description: typeof e.description === 'string' ? e.description : undefined, mimeType: typeof e.mimeType === 'string' ? e.mimeType : undefined, @@ -101,15 +115,38 @@ export function parseChallenge(raw: unknown): Challenge { extra: e.extra && typeof e.extra === 'object' && !Array.isArray(e.extra) ? (e.extra as AcceptEntry['extra']) : undefined, + raw: e, } satisfies AcceptEntry; }); return { x402Version: version, accepts: parsedAccepts, + resource: obj.resource, error: typeof obj.error === 'string' ? obj.error : undefined, }; } +/** + * Radius x402 flavor: SBC has no EIP-3009, so servers advertising + * `extra.settlementMethod: "permit-transferFrom"` settle via EIP-2612. + * The header keeps the flat v1-style envelope with a permit payload + * (`kind: "permit-eip2612"`) per the radius-dev integration guide. + */ +export function encodePermitPaymentHeader(args: { + x402Version: number; + scheme: string; + network: string; + payload: { kind: 'permit-eip2612'; [k: string]: unknown }; +}): string { + const json = JSON.stringify({ + x402Version: args.x402Version, + scheme: args.scheme, + network: args.network, + payload: args.payload, + }); + return Buffer.from(json, 'utf8').toString('base64'); +} + export function networkIdForChain(chainId: number): string { return `eip155:${chainId}`; } @@ -120,31 +157,61 @@ export function pickAccept(accepts: AcceptEntry[], chainId: number): AcceptEntry } export function encodePaymentHeader(payload: PaymentPayload): string { - const json = JSON.stringify({ - x402Version: payload.x402Version, - scheme: payload.scheme, - network: payload.network, - payload: { - signature: payload.payload.signature, - authorization: { - from: payload.payload.authorization.from, - to: payload.payload.authorization.to, - value: payload.payload.authorization.value.toString(), - validAfter: payload.payload.authorization.validAfter, - validBefore: payload.payload.authorization.validBefore, - nonce: payload.payload.authorization.nonce, - }, - }, - }); + const a = payload.payload.authorization; + // v2 (specs/schemes/exact/scheme_exact_evm.md): the header echoes the chosen + // accepts entry as `accepted` plus the challenge's `resource`, and the + // authorization validity bounds are strings. v1 keeps the flat envelope. + const json = payload.x402Version >= 2 + ? JSON.stringify({ + x402Version: payload.x402Version, + scheme: payload.scheme, + network: payload.network, + ...(payload.resource !== undefined ? { resource: payload.resource } : {}), + accepted: payload.accepted ?? { scheme: payload.scheme, network: payload.network }, + payload: { + signature: payload.payload.signature, + authorization: { + from: a.from, + to: a.to, + value: a.value.toString(), + validAfter: a.validAfter.toString(), + validBefore: a.validBefore.toString(), + nonce: a.nonce, + }, + }, + }) + : JSON.stringify({ + x402Version: payload.x402Version, + scheme: payload.scheme, + network: payload.network, + payload: { + signature: payload.payload.signature, + authorization: { + from: a.from, + to: a.to, + value: a.value.toString(), + validAfter: a.validAfter, + validBefore: a.validBefore, + nonce: a.nonce, + }, + }, + }); return Buffer.from(json, 'utf8').toString('base64'); } +function extractTxHash(obj: Record): string | undefined { + for (const key of ['transaction', 'txHash', 'transactionHash', 'hash']) { + if (typeof obj[key] === 'string') return obj[key]; + } + return undefined; +} + export function decodePaymentResponse(headerValue: string): PaymentResponseBody { const json = Buffer.from(headerValue, 'base64').toString('utf8'); const obj = JSON.parse(json) as Record; return { success: obj.success === true, - transaction: typeof obj.transaction === 'string' ? obj.transaction : undefined, + transaction: extractTxHash(obj), network: typeof obj.network === 'string' ? obj.network : undefined, payer: typeof obj.payer === 'string' ? obj.payer : undefined, errorReason: typeof obj.errorReason === 'string' ? obj.errorReason : null, diff --git a/src/types.ts b/src/types.ts index 0e31da7..1b22dfa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,8 @@ import type { Address, Chain, PublicClient, WalletClient } from 'viem'; export type NetworkName = 'mainnet' | 'testnet'; +export type WalletProviderName = 'keystore' | 'cdp' | 'para' | 'privy' | 'proxy'; + export interface ResolvedConfig { network: NetworkName; chain: Chain; @@ -10,6 +12,7 @@ export interface ResolvedConfig { rusdAddress?: Address; keystorePath: string; password?: string; + walletProvider: WalletProviderName; } export interface GlobalOptions { @@ -20,8 +23,11 @@ export interface GlobalOptions { rusd?: string; json?: boolean; wait?: boolean; + wallet?: string; } +export type { WalletProvider } from './lib/providers/types.js'; + export interface Clients { publicClient: PublicClient; walletClient?: WalletClient; diff --git a/tests/balance.test.ts b/tests/balance.test.ts new file mode 100644 index 0000000..8bbcbaa --- /dev/null +++ b/tests/balance.test.ts @@ -0,0 +1,57 @@ +import { describe, it, expect } from 'vitest'; +import { parseEther, parseUnits } from 'viem'; +import { splitAggregateBalance } from '../src/lib/balance.js'; + +const SBC_DECIMALS = 6; + +describe('splitAggregateBalance', () => { + it('does not double-count SBC included in the aggregate', () => { + // 0.1 SBC, no raw native: eth_getBalance returns 0.1 (token × rate). + const { totalWei, sbcAsWei, nativeWei } = splitAggregateBalance({ + aggregateWei: parseEther('0.1'), + sbcRaw: parseUnits('0.1', SBC_DECIMALS), + sbcDecimals: SBC_DECIMALS, + }); + expect(totalWei).toBe(parseEther('0.1')); + expect(sbcAsWei).toBe(parseEther('0.1')); + expect(nativeWei).toBe(0n); + }); + + it('derives the raw-native remainder when both are held', () => { + const { nativeWei } = splitAggregateBalance({ + aggregateWei: parseEther('0.25'), + sbcRaw: parseUnits('0.1', SBC_DECIMALS), + sbcDecimals: SBC_DECIMALS, + }); + expect(nativeWei).toBe(parseEther('0.15')); + }); + + it('clamps the native remainder at zero if the rate drifts below peg', () => { + const { nativeWei } = splitAggregateBalance({ + aggregateWei: parseEther('0.09'), + sbcRaw: parseUnits('0.1', SBC_DECIMALS), + sbcDecimals: SBC_DECIMALS, + }); + expect(nativeWei).toBe(0n); + }); + + it('handles zero balances', () => { + const { totalWei, sbcAsWei, nativeWei } = splitAggregateBalance({ + aggregateWei: 0n, + sbcRaw: 0n, + sbcDecimals: SBC_DECIMALS, + }); + expect(totalWei).toBe(0n); + expect(sbcAsWei).toBe(0n); + expect(nativeWei).toBe(0n); + }); + + it('scales correctly for 18-decimal tokens', () => { + const { sbcAsWei } = splitAggregateBalance({ + aggregateWei: parseEther('1'), + sbcRaw: parseEther('1'), + sbcDecimals: 18, + }); + expect(sbcAsWei).toBe(parseEther('1')); + }); +}); diff --git a/tests/providers.test.ts b/tests/providers.test.ts new file mode 100644 index 0000000..3ce29f8 --- /dev/null +++ b/tests/providers.test.ts @@ -0,0 +1,1145 @@ +import { describe, it, expect, vi } from 'vitest'; +import { existsSync, mkdtempSync, writeFileSync, unlinkSync, statSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { privateKeyToAccount } from 'viem/accounts'; +import { chainFor } from '../src/lib/chains.js'; +import type { ResolvedConfig, WalletProviderName } from '../src/types.js'; + +// config.ts captures RADIUS_HOME at module load, so isolate ~/.radius before +// dynamically importing anything that transitively loads it. +const radiusHome = mkdtempSync(join(tmpdir(), 'radius-cli-providers-')); +process.env.RADIUS_HOME = radiusHome; + +const { getProvider } = await import('../src/lib/providers/index.js'); +const { requireAccount, getOwnAddress } = await import('../src/lib/account.js'); +const { disableProviderTelemetry } = await import('../src/lib/providerTelemetry.js'); +const { resolveConfig, writeProviderConfig, deleteProviderConfig } = await import('../src/lib/config.js'); + +function makeCfg(overrides: Partial = {}): ResolvedConfig { + return { + network: 'testnet', + chain: chainFor('testnet'), + rpcUrl: 'http://localhost:0', + keystorePath: join(radiusHome, 'keystore.json'), + walletProvider: 'keystore', + ...overrides, + }; +} + +describe('privy provider', () => { + const provider = getProvider('privy'); + const privySessionPath = join(radiusHome, 'privy-session.json'); + const MOCK_ADDRESS = '0xabcdef1234567890abcdef1234567890abcdef12'; + const MOCK_WALLET_ID = 'wlt_test123'; + const MOCK_SESSION = { + walletId: MOCK_WALLET_ID, + address: MOCK_ADDRESS, + }; + + it('does not expose exportPrivateKey (remote key material)', () => { + expect(provider.exportPrivateKey).toBeUndefined(); + }); + + it('getAccount rejects when not logged in', async () => { + await expect(provider.getAccount(makeCfg({ walletProvider: 'privy' }))).rejects.toThrow( + /Not logged in to Privy/, + ); + }); + + it('getAddress rejects when not logged in', async () => { + await expect(provider.getAddress(makeCfg({ walletProvider: 'privy' }))).rejects.toThrow( + /Not logged in to Privy/, + ); + }); + + it('getAddress returns cached address from session file', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + try { + const address = await provider.getAddress(makeCfg({ walletProvider: 'privy' })); + expect(address.toLowerCase()).toBe(MOCK_ADDRESS.toLowerCase()); + } finally { + unlinkSync(privySessionPath); + } + }); + + it('getAccount rejects without credentials when session exists', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origId = process.env.PRIVY_APP_ID; + const origSecret = process.env.PRIVY_APP_SECRET; + delete process.env.PRIVY_APP_ID; + delete process.env.PRIVY_APP_SECRET; + try { + await expect(provider.getAccount(makeCfg({ walletProvider: 'privy' }))).rejects.toThrow( + /Privy credentials not configured/, + ); + } finally { + if (origId) process.env.PRIVY_APP_ID = origId; + if (origSecret) process.env.PRIVY_APP_SECRET = origSecret; + if (existsSync(privySessionPath)) unlinkSync(privySessionPath); + } + }); + + it('getAccount returns a viem-compatible account when credentials exist', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origId = process.env.PRIVY_APP_ID; + const origSecret = process.env.PRIVY_APP_SECRET; + process.env.PRIVY_APP_ID = 'test-app-id'; + process.env.PRIVY_APP_SECRET = 'test-app-secret'; + try { + const account = await provider.getAccount(makeCfg({ walletProvider: 'privy' })); + expect(account.address.toLowerCase()).toBe(MOCK_ADDRESS.toLowerCase()); + expect(typeof account.signMessage).toBe('function'); + expect(typeof account.signTransaction).toBe('function'); + expect(typeof account.signTypedData).toBe('function'); + } finally { + if (origId) process.env.PRIVY_APP_ID = origId; + else delete process.env.PRIVY_APP_ID; + if (origSecret) process.env.PRIVY_APP_SECRET = origSecret; + else delete process.env.PRIVY_APP_SECRET; + if (existsSync(privySessionPath)) unlinkSync(privySessionPath); + } + }); + + it('status shows not logged in', async () => { + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.status(makeCfg({ walletProvider: 'privy' }), {}); + } finally { + console.log = origLog; + } + expect(logs.some((l) => l.includes('privy'))).toBe(true); + expect(logs.some((l) => l.includes('not logged in'))).toBe(true); + }); + + it('status shows logged-in state from session file', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.status(makeCfg({ walletProvider: 'privy' }), {}); + } finally { + console.log = origLog; + unlinkSync(privySessionPath); + } + expect(logs.some((l) => l.includes(MOCK_ADDRESS))).toBe(true); + expect(logs.some((l) => l.includes(MOCK_WALLET_ID))).toBe(true); + }); + + it('status --json returns structured output', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.status(makeCfg({ walletProvider: 'privy' }), { json: true }); + } finally { + console.log = origLog; + unlinkSync(privySessionPath); + } + const parsed = JSON.parse(logs[0]); + expect(parsed.provider).toBe('privy'); + expect(parsed.loggedIn).toBe(true); + expect(parsed.address.toLowerCase()).toBe(MOCK_ADDRESS.toLowerCase()); + expect(parsed.walletId).toBe(MOCK_WALLET_ID); + }); + + it('logout is a no-op when not logged in', async () => { + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.logout!(makeCfg({ walletProvider: 'privy' })); + } finally { + console.log = origLog; + } + expect(logs.some((l) => l.includes('Not logged in'))).toBe(true); + }); + + it('logout deletes session file', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + expect(existsSync(privySessionPath)).toBe(true); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.logout!(makeCfg({ walletProvider: 'privy' })); + } finally { + console.log = origLog; + } + expect(existsSync(privySessionPath)).toBe(false); + expect(logs.some((l) => l.includes('Logged out'))).toBe(true); + }); + + it('session file is stored with restrictive permissions (0o600)', () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + try { + const stat = statSync(privySessionPath); + expect(stat.mode & 0o777).toBe(0o600); + } finally { + unlinkSync(privySessionPath); + } + }); + + it('--private-key overrides privy provider', async () => { + const PK = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'; + const cfg = makeCfg({ walletProvider: 'privy' }); + const account = await requireAccount(cfg, PK); + expect(account.address).toBe(privateKeyToAccount(PK).address); + }); + + it('provider resolution via requireAccount dispatches to privy', async () => { + const cfg = makeCfg({ walletProvider: 'privy' }); + await expect(requireAccount(cfg, undefined)).rejects.toThrow(/Not logged in to Privy/); + }); + + it('signMessage calls Privy personal_sign RPC', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origId = process.env.PRIVY_APP_ID; + const origSecret = process.env.PRIVY_APP_SECRET; + process.env.PRIVY_APP_ID = 'test-app-id'; + process.env.PRIVY_APP_SECRET = 'test-app-secret'; + + const mockSig = '0x' + 'ab'.repeat(65); + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ data: { signature: mockSig } }), + }) as any; + + try { + const account = await provider.getAccount(makeCfg({ walletProvider: 'privy' })); + const sig = await account.signMessage({ message: 'hello' }); + expect(sig).toBe(mockSig); + + const fetchCall = (globalThis.fetch as any).mock.calls[0]; + expect(fetchCall[0]).toContain(`/wallets/${MOCK_WALLET_ID}/rpc`); + const body = JSON.parse(fetchCall[1].body); + expect(body.method).toBe('personal_sign'); + expect(body.params.encoding).toBe('hex'); + } finally { + globalThis.fetch = origFetch; + if (origId) process.env.PRIVY_APP_ID = origId; + else delete process.env.PRIVY_APP_ID; + if (origSecret) process.env.PRIVY_APP_SECRET = origSecret; + else delete process.env.PRIVY_APP_SECRET; + if (existsSync(privySessionPath)) unlinkSync(privySessionPath); + } + }); + + it('signTypedData calls Privy eth_signTypedData_v4 RPC', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origId = process.env.PRIVY_APP_ID; + const origSecret = process.env.PRIVY_APP_SECRET; + process.env.PRIVY_APP_ID = 'test-app-id'; + process.env.PRIVY_APP_SECRET = 'test-app-secret'; + + const mockSig = '0x' + 'cd'.repeat(65); + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ data: { signature: mockSig } }), + }) as any; + + try { + const account = await provider.getAccount(makeCfg({ walletProvider: 'privy' })); + const sig = await account.signTypedData({ + domain: { name: 'Test', version: '1', chainId: 1 }, + types: { Foo: [{ name: 'bar', type: 'uint256' }] }, + primaryType: 'Foo', + message: { bar: 42n }, + }); + expect(sig).toBe(mockSig); + + const fetchCall = (globalThis.fetch as any).mock.calls[0]; + const body = JSON.parse(fetchCall[1].body); + expect(body.method).toBe('eth_signTypedData_v4'); + expect(body.params.typed_data.primary_type).toBe('Foo'); + } finally { + globalThis.fetch = origFetch; + if (origId) process.env.PRIVY_APP_ID = origId; + else delete process.env.PRIVY_APP_ID; + if (origSecret) process.env.PRIVY_APP_SECRET = origSecret; + else delete process.env.PRIVY_APP_SECRET; + if (existsSync(privySessionPath)) unlinkSync(privySessionPath); + } + }); + + it('signTransaction calls Privy secp256k1_sign RPC', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origId = process.env.PRIVY_APP_ID; + const origSecret = process.env.PRIVY_APP_SECRET; + process.env.PRIVY_APP_ID = 'test-app-id'; + process.env.PRIVY_APP_SECRET = 'test-app-secret'; + + // 65-byte signature: r (32) + s (32) + v (1) + const mockR = 'ab'.repeat(32); + const mockS = 'cd'.repeat(32); + const mockV = '1b'; // v = 27 + const mockSig = `0x${mockR}${mockS}${mockV}`; + + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ data: { signature: mockSig } }), + }) as any; + + try { + const account = await provider.getAccount(makeCfg({ walletProvider: 'privy' })); + const result = await account.signTransaction({ + to: '0x0000000000000000000000000000000000000001', + value: 0n, + nonce: 0, + gas: 21000n, + gasPrice: 1000000000n, + chainId: 72344, + type: 'legacy', + } as any); + + expect(result).toMatch(/^0x/); + const fetchCall = (globalThis.fetch as any).mock.calls[0]; + const body = JSON.parse(fetchCall[1].body); + expect(body.method).toBe('secp256k1_sign'); + expect(body.params.hash).toMatch(/^0x[0-9a-f]{64}$/); + } finally { + globalThis.fetch = origFetch; + if (origId) process.env.PRIVY_APP_ID = origId; + else delete process.env.PRIVY_APP_ID; + if (origSecret) process.env.PRIVY_APP_SECRET = origSecret; + else delete process.env.PRIVY_APP_SECRET; + if (existsSync(privySessionPath)) unlinkSync(privySessionPath); + } + }); + + it('RPC errors include Privy error details', async () => { + writeFileSync(privySessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origId = process.env.PRIVY_APP_ID; + const origSecret = process.env.PRIVY_APP_SECRET; + process.env.PRIVY_APP_ID = 'test-app-id'; + process.env.PRIVY_APP_SECRET = 'test-app-secret'; + + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn().mockResolvedValue({ + ok: false, + status: 403, + text: () => Promise.resolve(JSON.stringify({ error: { message: 'policy violation' } })), + }) as any; + + try { + const account = await provider.getAccount(makeCfg({ walletProvider: 'privy' })); + await expect(account.signMessage({ message: 'test' })).rejects.toThrow( + /Privy RPC personal_sign failed \(403\): policy violation/, + ); + } finally { + globalThis.fetch = origFetch; + if (origId) process.env.PRIVY_APP_ID = origId; + else delete process.env.PRIVY_APP_ID; + if (origSecret) process.env.PRIVY_APP_SECRET = origSecret; + else delete process.env.PRIVY_APP_SECRET; + if (existsSync(privySessionPath)) unlinkSync(privySessionPath); + } + }); +}); + +describe('cdp provider', () => { + const provider = getProvider('cdp'); + const cdpSessionPath = join(radiusHome, 'cdp-session.json'); + const MOCK_CDP_ADDRESS = '0xabcdef1234567890abcdef1234567890abcdef12'; + const MOCK_CDP_SESSION = { + address: MOCK_CDP_ADDRESS, + accountName: 'test-account', + }; + + it('getAccount rejects when not logged in', async () => { + await expect(provider.getAccount(makeCfg({ walletProvider: 'cdp' }))).rejects.toThrow( + /Not logged in to CDP/, + ); + }); + + it('getAddress rejects when not logged in', async () => { + await expect(provider.getAddress(makeCfg({ walletProvider: 'cdp' }))).rejects.toThrow( + /Not logged in to CDP/, + ); + }); + + it('does not expose exportPrivateKey (server-side keys)', () => { + expect(provider.exportPrivateKey).toBeUndefined(); + }); + + it('getAccount rejects without credentials when session exists', async () => { + writeFileSync(cdpSessionPath, JSON.stringify(MOCK_CDP_SESSION), { mode: 0o600 }); + const origId = process.env.CDP_API_KEY_ID; + const origSecret = process.env.CDP_API_KEY_SECRET; + const origWallet = process.env.CDP_WALLET_SECRET; + delete process.env.CDP_API_KEY_ID; + delete process.env.CDP_API_KEY_SECRET; + delete process.env.CDP_WALLET_SECRET; + try { + await expect(provider.getAccount(makeCfg({ walletProvider: 'cdp' }))).rejects.toThrow( + /CDP credentials not configured/, + ); + } finally { + if (origId) process.env.CDP_API_KEY_ID = origId; + if (origSecret) process.env.CDP_API_KEY_SECRET = origSecret; + if (origWallet) process.env.CDP_WALLET_SECRET = origWallet; + if (existsSync(cdpSessionPath)) unlinkSync(cdpSessionPath); + } + }); + + it('status shows not logged in', async () => { + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.status(makeCfg({ walletProvider: 'cdp' }), {}); + } finally { + console.log = origLog; + } + expect(logs.some((l) => l.includes('cdp'))).toBe(true); + expect(logs.some((l) => l.includes('not logged in'))).toBe(true); + }); + + it('logout is a no-op when not logged in', async () => { + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.logout!(makeCfg({ walletProvider: 'cdp' })); + } finally { + console.log = origLog; + } + expect(logs.some((l) => l.includes('Not logged in'))).toBe(true); + }); + + it('getAddress returns cached address from session file', async () => { + writeFileSync(cdpSessionPath, JSON.stringify(MOCK_CDP_SESSION), { mode: 0o600 }); + try { + const address = await provider.getAddress(makeCfg({ walletProvider: 'cdp' })); + expect(address.toLowerCase()).toBe(MOCK_CDP_ADDRESS.toLowerCase()); + } finally { + unlinkSync(cdpSessionPath); + } + }); + + it('status shows logged-in state from session file', async () => { + writeFileSync(cdpSessionPath, JSON.stringify(MOCK_CDP_SESSION), { mode: 0o600 }); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.status(makeCfg({ walletProvider: 'cdp' }), {}); + } finally { + console.log = origLog; + unlinkSync(cdpSessionPath); + } + expect(logs.some((l) => l.includes(MOCK_CDP_ADDRESS))).toBe(true); + expect(logs.some((l) => l.includes('test-account'))).toBe(true); + }); + + it('status --json returns structured output', async () => { + writeFileSync(cdpSessionPath, JSON.stringify(MOCK_CDP_SESSION), { mode: 0o600 }); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.status(makeCfg({ walletProvider: 'cdp' }), { json: true }); + } finally { + console.log = origLog; + unlinkSync(cdpSessionPath); + } + const parsed = JSON.parse(logs[0]); + expect(parsed.provider).toBe('cdp'); + expect(parsed.loggedIn).toBe(true); + expect(parsed.address.toLowerCase()).toBe(MOCK_CDP_ADDRESS.toLowerCase()); + }); + + it('logout deletes session file', async () => { + writeFileSync(cdpSessionPath, JSON.stringify(MOCK_CDP_SESSION), { mode: 0o600 }); + expect(existsSync(cdpSessionPath)).toBe(true); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.logout!(makeCfg({ walletProvider: 'cdp' })); + } finally { + console.log = origLog; + } + expect(existsSync(cdpSessionPath)).toBe(false); + expect(logs.some((l) => l.includes('Logged out'))).toBe(true); + }); + + it('--private-key overrides cdp provider', async () => { + const PK = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'; + const cfg = makeCfg({ walletProvider: 'cdp' }); + const account = await requireAccount(cfg, PK); + expect(account.address).toBe(privateKeyToAccount(PK).address); + }); +}); + +describe('para provider', () => { + const provider = getProvider('para'); + const paraSessionPath = join(radiusHome, 'para-session.json'); + const paraBackupPath = join(radiusHome, 'para-session.bak.json'); + const MOCK_ADDRESS = '0x1234567890abcdef1234567890abcdef12345678'; + const MOCK_SESSION = { + email: 'test@example.com', + userShare: 'mock-user-share-base64', + walletId: 'mock-wallet-id', + address: MOCK_ADDRESS, + }; + + it('getAccount rejects when not logged in', async () => { + await expect(provider.getAccount(makeCfg({ walletProvider: 'para' }))).rejects.toThrow( + /Not logged in to Para/, + ); + }); + + it('getAddress rejects when not logged in', async () => { + await expect(provider.getAddress(makeCfg({ walletProvider: 'para' }))).rejects.toThrow( + /Not logged in to Para/, + ); + }); + + it('does not expose exportPrivateKey (MPC key material)', () => { + expect(provider.exportPrivateKey).toBeUndefined(); + }); + + it('getAccount rejects without API key when session exists', async () => { + writeFileSync(paraSessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origKey = process.env.PARA_API_KEY; + delete process.env.PARA_API_KEY; + try { + await expect(provider.getAccount(makeCfg({ walletProvider: 'para' }))).rejects.toThrow( + /Para API key not configured/, + ); + } finally { + if (origKey) process.env.PARA_API_KEY = origKey; + if (existsSync(paraSessionPath)) unlinkSync(paraSessionPath); + } + }); + + it('status shows not logged in', async () => { + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.status(makeCfg({ walletProvider: 'para' }), {}); + } finally { + console.log = origLog; + } + expect(logs.some((l) => l.includes('para'))).toBe(true); + expect(logs.some((l) => l.includes('not logged in'))).toBe(true); + }); + + it('logout is a no-op when not logged in', async () => { + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.logout!(makeCfg({ walletProvider: 'para' })); + } finally { + console.log = origLog; + } + expect(logs.some((l) => l.includes('Not logged in'))).toBe(true); + }); + + it('reset reports the archived session backup path', async () => { + writeFileSync(paraSessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origKey = process.env.PARA_API_KEY; + delete process.env.PARA_API_KEY; + + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await expect(provider.login!(makeCfg({ walletProvider: 'para' }), { reset: true })).rejects.toThrow( + /Para API key not configured/, + ); + } finally { + console.log = origLog; + if (origKey) process.env.PARA_API_KEY = origKey; + if (existsSync(paraSessionPath)) unlinkSync(paraSessionPath); + if (existsSync(paraBackupPath)) unlinkSync(paraBackupPath); + } + + expect(logs.some((l) => l.includes(`Previous Para session archived to ${paraBackupPath}.`))).toBe(true); + }); + + it('login restores an archived session backup', async () => { + writeFileSync(paraBackupPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.login!(makeCfg({ walletProvider: 'para' }), {}); + expect(existsSync(paraSessionPath)).toBe(true); + expect(existsSync(paraBackupPath)).toBe(false); + } finally { + console.log = origLog; + if (existsSync(paraSessionPath)) unlinkSync(paraSessionPath); + if (existsSync(paraBackupPath)) unlinkSync(paraBackupPath); + } + + expect(logs.some((l) => l.includes(`Restored Para session from ${paraBackupPath}.`))).toBe(true); + }); + + it('getAddress returns cached address from session file', async () => { + writeFileSync(paraSessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + try { + const address = await provider.getAddress(makeCfg({ walletProvider: 'para' })); + expect(address.toLowerCase()).toBe(MOCK_ADDRESS.toLowerCase()); + } finally { + unlinkSync(paraSessionPath); + } + }); + + it('session file is stored with restrictive permissions (0o600)', () => { + writeFileSync(paraSessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + try { + const stat = statSync(paraSessionPath); + // 0o600 = owner read+write only (octal 33216 on some systems, mask with 0o777) + expect(stat.mode & 0o777).toBe(0o600); + } finally { + unlinkSync(paraSessionPath); + } + }); + + it('status shows logged-in state from session file', async () => { + writeFileSync(paraSessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.status(makeCfg({ walletProvider: 'para' }), {}); + } finally { + console.log = origLog; + unlinkSync(paraSessionPath); + } + expect(logs.some((l) => l.includes('test@example.com'))).toBe(true); + expect(logs.some((l) => l.includes(MOCK_ADDRESS))).toBe(true); + }); + + it('status --json returns structured output', async () => { + writeFileSync(paraSessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.status(makeCfg({ walletProvider: 'para' }), { json: true }); + } finally { + console.log = origLog; + unlinkSync(paraSessionPath); + } + const parsed = JSON.parse(logs[0]); + expect(parsed.provider).toBe('para'); + expect(parsed.loggedIn).toBe(true); + expect(parsed.email).toBe('test@example.com'); + expect(parsed.address.toLowerCase()).toBe(MOCK_ADDRESS.toLowerCase()); + }); + + it('logout archives session file', async () => { + writeFileSync(paraSessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + expect(existsSync(paraSessionPath)).toBe(true); + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + try { + await provider.logout!(makeCfg({ walletProvider: 'para' })); + } finally { + console.log = origLog; + if (existsSync(paraBackupPath)) unlinkSync(paraBackupPath); + } + expect(existsSync(paraSessionPath)).toBe(false); + expect(logs.some((l) => l.includes(`Session archived to ${paraBackupPath}.`))).toBe(true); + expect(logs.some((l) => l.includes('Logged out'))).toBe(true); + }); + + it('getAccount rejects with API key error when session exists but no key (env or config)', async () => { + writeFileSync(paraSessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origKey = process.env.PARA_API_KEY; + delete process.env.PARA_API_KEY; + try { + await expect(provider.getAccount(makeCfg({ walletProvider: 'para' }))).rejects.toThrow( + /Para API key not configured/, + ); + } finally { + if (origKey) process.env.PARA_API_KEY = origKey; + if (existsSync(paraSessionPath)) unlinkSync(paraSessionPath); + } + }); + + it('provider resolution via requireAccount dispatches to para', async () => { + const cfg = makeCfg({ walletProvider: 'para' }); + await expect(requireAccount(cfg, undefined)).rejects.toThrow(/Not logged in to Para/); + }); + + it('--private-key overrides para provider', async () => { + const PK = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'; + const cfg = makeCfg({ walletProvider: 'para' }); + const account = await requireAccount(cfg, PK); + expect(account.address).toBe(privateKeyToAccount(PK).address); + }); + + it('getAccount passes session address to createParaViemAccount', async () => { + writeFileSync(paraSessionPath, JSON.stringify(MOCK_SESSION), { mode: 0o600 }); + const origKey = process.env.PARA_API_KEY; + process.env.PARA_API_KEY = 'test-key'; + + // Mock the Para SDK dynamic imports + const mockSetUserShare = vi.fn().mockResolvedValue(undefined); + const mockSetCurrentWalletIds = vi.fn().mockResolvedValue(undefined); + const mockCreateParaViemAccount = vi.fn().mockReturnValue({ + address: MOCK_ADDRESS, + type: 'local', + signMessage: vi.fn(), + signTransaction: vi.fn(), + signTypedData: vi.fn(), + sign: vi.fn(), + }); + + const mockPara = vi.fn().mockImplementation(() => ({ + setUserShare: mockSetUserShare, + setCurrentWalletIds: mockSetCurrentWalletIds, + })); + + vi.doMock('@getpara/server-sdk', () => ({ + Para: mockPara, + Environment: { BETA: 'BETA', PROD: 'PROD', DEV: 'DEV', SANDBOX: 'SANDBOX' }, + })); + vi.doMock('@getpara/viem-v2-integration', () => ({ + createParaViemAccount: mockCreateParaViemAccount, + })); + + try { + // Re-import to pick up mocks + const { paraProvider } = await import('../src/lib/providers/para.js'); + await paraProvider.getAccount(makeCfg({ walletProvider: 'para' })); + + expect(mockPara).toHaveBeenCalledWith('BETA', 'test-key', { + disableWorkers: true, + disableWebSockets: true, + }); + expect(mockSetUserShare).toHaveBeenCalledWith(MOCK_SESSION.userShare); + expect(mockSetCurrentWalletIds).toHaveBeenCalledWith({ EVM: [MOCK_SESSION.walletId] }); + expect(mockCreateParaViemAccount).toHaveBeenCalledWith( + expect.objectContaining({ address: MOCK_ADDRESS }), + ); + } finally { + vi.doUnmock('@getpara/server-sdk'); + vi.doUnmock('@getpara/viem-v2-integration'); + if (origKey) process.env.PARA_API_KEY = origKey; + else delete process.env.PARA_API_KEY; + if (existsSync(paraSessionPath)) unlinkSync(paraSessionPath); + } + }); +}); + +describe('proxy provider', () => { + const provider = getProvider('proxy'); + const MOCK_ADDRESS = '0xabcdef1234567890abcdef1234567890abcdef12'; + const MOCK_SIGNATURE = `0x${'ab'.repeat(32)}${'cd'.repeat(32)}1b`; + + function jsonResponse(body: unknown, status = 200): Response { + return { + ok: status >= 200 && status < 300, + status, + statusText: status >= 200 && status < 300 ? 'OK' : 'Error', + json: () => Promise.resolve(body), + text: () => Promise.resolve(JSON.stringify(body)), + } as Response; + } + + function saveProxyEnv(): Record { + return { + RADIUS_WALLET: process.env.RADIUS_WALLET, + RADIUS_WALLET_PROXY_URL: process.env.RADIUS_WALLET_PROXY_URL, + RADIUS_WALLET_ALIAS: process.env.RADIUS_WALLET_ALIAS, + RADIUS_WALLET_PROXY_TOKEN: process.env.RADIUS_WALLET_PROXY_TOKEN, + CF_ACCESS_CLIENT_ID: process.env.CF_ACCESS_CLIENT_ID, + CF_ACCESS_CLIENT_SECRET: process.env.CF_ACCESS_CLIENT_SECRET, + }; + } + + function restoreProxyEnv(saved: Record): void { + for (const [key, value] of Object.entries(saved)) { + if (value === undefined) delete process.env[key]; + else process.env[key] = value; + } + } + + function configureProxyEnv(): Record { + const saved = saveProxyEnv(); + process.env.RADIUS_WALLET_PROXY_URL = 'https://wallet-proxy.example/'; + process.env.RADIUS_WALLET_ALIAS = 'agent0-main'; + process.env.RADIUS_WALLET_PROXY_TOKEN = 'test-token'; + process.env.CF_ACCESS_CLIENT_ID = 'access-client-id'; + process.env.CF_ACCESS_CLIENT_SECRET = 'access-client-secret'; + return saved; + } + + it('--wallet proxy resolves through config selection', () => { + const cfg = resolveConfig({ wallet: 'proxy' }); + expect(cfg.walletProvider).toBe('proxy'); + }); + + it('RADIUS_WALLET=proxy resolves through env selection', () => { + const saved = saveProxyEnv(); + process.env.RADIUS_WALLET = 'proxy'; + try { + const cfg = resolveConfig({}); + expect(cfg.walletProvider).toBe('proxy'); + } finally { + restoreProxyEnv(saved); + } + }); + + it('does not expose exportPrivateKey (remote secret boundary)', () => { + expect(provider.exportPrivateKey).toBeUndefined(); + }); + + it('getAddress rejects when URL or alias is missing', async () => { + const saved = saveProxyEnv(); + delete process.env.RADIUS_WALLET_PROXY_URL; + delete process.env.RADIUS_WALLET_ALIAS; + try { + await expect(provider.getAddress(makeCfg({ walletProvider: 'proxy' }))).rejects.toThrow( + /Proxy wallet not configured/, + ); + } finally { + restoreProxyEnv(saved); + } + }); + + it('getAddress calls the proxy address endpoint with optional auth headers', async () => { + const saved = configureProxyEnv(); + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn().mockResolvedValue(jsonResponse({ + provider: 'cdp', + alias: 'agent0-main', + address: MOCK_ADDRESS, + })) as any; + + try { + const address = await provider.getAddress(makeCfg({ walletProvider: 'proxy' })); + expect(address.toLowerCase()).toBe(MOCK_ADDRESS.toLowerCase()); + + const fetchCall = (globalThis.fetch as any).mock.calls[0]; + expect(fetchCall[0]).toBe('https://wallet-proxy.example/v1/wallets/agent0-main/address'); + expect(fetchCall[1].method).toBe('GET'); + expect(fetchCall[1].headers.authorization).toBe('Bearer test-token'); + expect(fetchCall[1].headers['CF-Access-Client-Id']).toBe('access-client-id'); + expect(fetchCall[1].headers['CF-Access-Client-Secret']).toBe('access-client-secret'); + } finally { + globalThis.fetch = origFetch; + restoreProxyEnv(saved); + } + }); + + it('can read proxy URL and alias from providers.proxy config', async () => { + const saved = saveProxyEnv(); + delete process.env.RADIUS_WALLET_PROXY_URL; + delete process.env.RADIUS_WALLET_ALIAS; + writeProviderConfig('proxy', { + url: 'https://config-wallet-proxy.example/', + alias: 'config-agent', + token: 'config-token', + }); + + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn().mockResolvedValue(jsonResponse({ + provider: 'privy', + alias: 'config-agent', + address: MOCK_ADDRESS, + })) as any; + + try { + const address = await provider.getAddress(makeCfg({ walletProvider: 'proxy' })); + expect(address.toLowerCase()).toBe(MOCK_ADDRESS.toLowerCase()); + const fetchCall = (globalThis.fetch as any).mock.calls[0]; + expect(fetchCall[0]).toBe('https://config-wallet-proxy.example/v1/wallets/config-agent/address'); + expect(fetchCall[1].headers.authorization).toBe('Bearer config-token'); + } finally { + globalThis.fetch = origFetch; + deleteProviderConfig('proxy'); + restoreProxyEnv(saved); + } + }); + + it('signMessage posts to the proxy sign-message endpoint', async () => { + const saved = configureProxyEnv(); + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn((url: string) => { + if (url.endsWith('/address')) { + return Promise.resolve(jsonResponse({ provider: 'privy', alias: 'agent0-main', address: MOCK_ADDRESS })); + } + return Promise.resolve(jsonResponse({ provider: 'privy', alias: 'agent0-main', signature: MOCK_SIGNATURE })); + }) as any; + + try { + const account = await provider.getAccount(makeCfg({ walletProvider: 'proxy' })); + const signature = await account.signMessage({ message: 'hello' }); + expect(signature).toBe(MOCK_SIGNATURE); + + const signCall = (globalThis.fetch as any).mock.calls.find((call: any[]) => call[0].endsWith('/sign-message')); + expect(signCall[1].method).toBe('POST'); + expect(JSON.parse(signCall[1].body)).toEqual({ message: 'hello' }); + } finally { + globalThis.fetch = origFetch; + restoreProxyEnv(saved); + } + }); + + it('signTypedData posts typed data to the proxy', async () => { + const saved = configureProxyEnv(); + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn((url: string) => { + if (url.endsWith('/address')) { + return Promise.resolve(jsonResponse({ provider: 'cdp', alias: 'agent0-main', address: MOCK_ADDRESS })); + } + return Promise.resolve(jsonResponse({ provider: 'cdp', alias: 'agent0-main', signature: MOCK_SIGNATURE })); + }) as any; + + try { + const account = await provider.getAccount(makeCfg({ walletProvider: 'proxy' })); + const signature = await account.signTypedData({ + domain: { name: 'Test', version: '1', chainId: 72344 }, + types: { Permit: [{ name: 'amount', type: 'uint256' }] }, + primaryType: 'Permit', + message: { amount: 42n }, + }); + expect(signature).toBe(MOCK_SIGNATURE); + + const signCall = (globalThis.fetch as any).mock.calls.find((call: any[]) => call[0].endsWith('/sign-typed-data')); + const body = JSON.parse(signCall[1].body); + expect(body.typedData.primaryType).toBe('Permit'); + expect(body.typedData.message.amount).toBe('42'); + } finally { + globalThis.fetch = origFetch; + restoreProxyEnv(saved); + } + }); + + it('sign({ hash }) posts hash mode to the proxy sign-transaction endpoint', async () => { + const saved = configureProxyEnv(); + const hash = `0x${'12'.repeat(32)}`; + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn((url: string) => { + if (url.endsWith('/address')) { + return Promise.resolve(jsonResponse({ provider: 'cdp', alias: 'agent0-main', address: MOCK_ADDRESS })); + } + return Promise.resolve(jsonResponse({ provider: 'cdp', alias: 'agent0-main', signature: MOCK_SIGNATURE })); + }) as any; + + try { + const account = await provider.getAccount(makeCfg({ walletProvider: 'proxy' })); + const signature = await (account as any).sign({ hash }); + expect(signature).toBe(MOCK_SIGNATURE); + + const signCall = (globalThis.fetch as any).mock.calls.find((call: any[]) => call[0].endsWith('/sign-transaction')); + expect(JSON.parse(signCall[1].body)).toEqual({ hash }); + } finally { + globalThis.fetch = origFetch; + restoreProxyEnv(saved); + } + }); + + it('signTransaction uses legacy transaction hash signing through the proxy', async () => { + const saved = configureProxyEnv(); + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn((url: string) => { + if (url.endsWith('/address')) { + return Promise.resolve(jsonResponse({ provider: 'cdp', alias: 'agent0-main', address: MOCK_ADDRESS })); + } + return Promise.resolve(jsonResponse({ provider: 'cdp', alias: 'agent0-main', signature: MOCK_SIGNATURE })); + }) as any; + + try { + const account = await provider.getAccount(makeCfg({ walletProvider: 'proxy' })); + const signed = await account.signTransaction({ + to: '0x0000000000000000000000000000000000000001', + value: 0n, + nonce: 0, + gas: 21000n, + gasPrice: 1000000000n, + chainId: 72344, + type: 'legacy', + } as any); + expect(signed).toMatch(/^0x/); + + const signCall = (globalThis.fetch as any).mock.calls.find((call: any[]) => call[0].endsWith('/sign-transaction')); + expect(JSON.parse(signCall[1].body).hash).toMatch(/^0x[0-9a-f]{64}$/); + } finally { + globalThis.fetch = origFetch; + restoreProxyEnv(saved); + } + }); + + it('status --json includes sanitized proxy config and remote capabilities', async () => { + const saved = configureProxyEnv(); + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn((url: string) => { + if (url.endsWith('/status')) { + return Promise.resolve(jsonResponse({ + provider: 'para', + alias: 'agent0-main', + status: 'configured', + address: MOCK_ADDRESS, + })); + } + return Promise.resolve(jsonResponse({ + provider: 'para', + alias: 'agent0-main', + capabilities: { + address: true, + signMessage: true, + signTypedData: true, + signTransaction: true, + exportPrivateKey: false, + }, + })); + }) as any; + + const logs: string[] = []; + const origLog = console.log; + console.log = (...args: any[]) => logs.push(args.join(' ')); + + try { + await provider.status(makeCfg({ walletProvider: 'proxy' }), { json: true }); + const parsed = JSON.parse(logs[0]); + expect(parsed.provider).toBe('proxy'); + expect(parsed.configured).toBe(true); + expect(parsed.loggedIn).toBe(true); + expect(parsed.alias).toBe('agent0-main'); + expect(parsed.url).toBe('https://wallet-proxy.example'); + expect(parsed.remoteProvider).toBe('para'); + expect(parsed.address.toLowerCase()).toBe(MOCK_ADDRESS.toLowerCase()); + expect(parsed.capabilities.exportPrivateKey).toBe(false); + expect(JSON.stringify(parsed)).not.toContain('test-token'); + expect(JSON.stringify(parsed)).not.toContain('access-client-secret'); + } finally { + console.log = origLog; + globalThis.fetch = origFetch; + restoreProxyEnv(saved); + } + }); + + it('proxy JSON errors include provider error code and message', async () => { + const saved = configureProxyEnv(); + const origFetch = globalThis.fetch; + globalThis.fetch = vi.fn().mockResolvedValue(jsonResponse({ + error: { + code: 'WALLET_NOT_FOUND', + message: 'Unknown wallet alias: agent9-main', + }, + }, 404)) as any; + + try { + await expect(provider.getAddress(makeCfg({ walletProvider: 'proxy' }))).rejects.toThrow( + /WALLET_NOT_FOUND: Unknown wallet alias: agent9-main/, + ); + } finally { + globalThis.fetch = origFetch; + restoreProxyEnv(saved); + } + }); +}); + +describe('provider telemetry controls', () => { + it('sets CDP analytics opt-outs by default', () => { + const origErrorReporting = process.env.DISABLE_CDP_ERROR_REPORTING; + const origUsageTracking = process.env.DISABLE_CDP_USAGE_TRACKING; + delete process.env.DISABLE_CDP_ERROR_REPORTING; + delete process.env.DISABLE_CDP_USAGE_TRACKING; + try { + disableProviderTelemetry('cdp'); + expect(process.env.DISABLE_CDP_ERROR_REPORTING).toBe('true'); + expect(process.env.DISABLE_CDP_USAGE_TRACKING).toBe('true'); + } finally { + if (origErrorReporting === undefined) delete process.env.DISABLE_CDP_ERROR_REPORTING; + else process.env.DISABLE_CDP_ERROR_REPORTING = origErrorReporting; + if (origUsageTracking === undefined) delete process.env.DISABLE_CDP_USAGE_TRACKING; + else process.env.DISABLE_CDP_USAGE_TRACKING = origUsageTracking; + } + }); + + it('sets Para OpenTelemetry opt-outs by default', () => { + const origSdkDisabled = process.env.OTEL_SDK_DISABLED; + const origTracesExporter = process.env.OTEL_TRACES_EXPORTER; + delete process.env.OTEL_SDK_DISABLED; + delete process.env.OTEL_TRACES_EXPORTER; + try { + disableProviderTelemetry('para'); + expect(process.env.OTEL_SDK_DISABLED).toBe('true'); + expect(process.env.OTEL_TRACES_EXPORTER).toBe('none'); + } finally { + if (origSdkDisabled === undefined) delete process.env.OTEL_SDK_DISABLED; + else process.env.OTEL_SDK_DISABLED = origSdkDisabled; + if (origTracesExporter === undefined) delete process.env.OTEL_TRACES_EXPORTER; + else process.env.OTEL_TRACES_EXPORTER = origTracesExporter; + } + }); +}); + +describe('keystore provider', () => { + const provider = getProvider('keystore'); + + it('auto-creates a keystore on first getAddress and reuses it for getAccount', async () => { + const cfg = makeCfg(); + expect(existsSync(cfg.keystorePath)).toBe(false); + + const address = await provider.getAddress(cfg); + expect(address).toMatch(/^0x[0-9a-fA-F]{40}$/); + expect(existsSync(cfg.keystorePath)).toBe(true); + + // Passwordless flag was persisted, so this must not prompt. + const account = await provider.getAccount(cfg); + expect(account.address.toLowerCase()).toBe(address.toLowerCase()); + }, 20_000); + + it('exportPrivateKey returns the key backing the keystore address', async () => { + const cfg = makeCfg(); + expect(provider.exportPrivateKey).toBeDefined(); + const pk = await provider.exportPrivateKey!(cfg); + const address = await provider.getAddress(cfg); + expect(privateKeyToAccount(pk).address.toLowerCase()).toBe(address.toLowerCase()); + }, 20_000); + + it('auto-creates with an explicit password and rejects a wrong one', async () => { + const cfg = makeCfg({ + keystorePath: join(radiusHome, 'keystore-pw.json'), + password: 'correct-horse-battery-staple', + }); + + const account = await provider.getAccount(cfg); + const reloaded = await provider.getAccount(cfg); + expect(reloaded.address).toBe(account.address); + + await expect(provider.getAccount({ ...cfg, password: 'wrong' })).rejects.toThrow(); + }, 60_000); +}); + +describe('account shims (requireAccount / getOwnAddress)', () => { + const PK = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'; + const PK_ADDRESS = privateKeyToAccount(PK).address; + + it('dispatch to the selected provider', async () => { + const cfg = makeCfg({ walletProvider: 'privy' }); + await expect(requireAccount(cfg, undefined)).rejects.toThrow(/Not logged in to Privy/); + await expect(getOwnAddress(cfg, undefined)).rejects.toThrow(/Not logged in to Privy/); + }); + + it('--private-key overrides the provider entirely', async () => { + const cfg = makeCfg({ walletProvider: 'cdp' }); + const account = await requireAccount(cfg, PK); + expect(account.address).toBe(PK_ADDRESS); + expect(await getOwnAddress(cfg, PK)).toBe(PK_ADDRESS); + }); + + it('normalizes a private key missing its 0x prefix', async () => { + const account = await requireAccount(makeCfg(), PK.slice(2)); + expect(account.address).toBe(PK_ADDRESS); + }); +}); diff --git a/tests/x402-eip2612.test.ts b/tests/x402-eip2612.test.ts new file mode 100644 index 0000000..2eb6a63 --- /dev/null +++ b/tests/x402-eip2612.test.ts @@ -0,0 +1,80 @@ +import { describe, it, expect } from 'vitest'; +import { recoverTypedDataAddress, type Address, type Hex } from 'viem'; +import { privateKeyToAccount } from 'viem/accounts'; +import { EIP2612_TYPES, signPermit } from '../src/lib/x402/eip2612.js'; +import { encodePermitPaymentHeader } from '../src/lib/x402/protocol.js'; + +const PK = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d' as Hex; +const ASSET = '0x33ad9e4BD16B69B5BFdED37D8B5D9fF9aba014Fb' as Address; +const SPENDER = '0x2c88F72487690c5F4C933c7b519027D787F00DA7' as Address; + +describe('signPermit', () => { + it('produces a v/r/s signature recoverable to the owner', async () => { + const account = privateKeyToAccount(PK); + const permit = await signPermit(account, { + asset: ASSET, + chainId: 723487, + name: 'Stable Coin', + version: '1', + spender: SPENDER, + value: 1000n, + nonce: 0n, + deadline: 9999999999n, + }); + + expect(permit.kind).toBe('permit-eip2612'); + expect(permit.owner).toBe(account.address); + expect(permit.spender).toBe(SPENDER); + expect(permit.value).toBe('1000'); + expect(permit.nonce).toBe('0'); + expect(permit.deadline).toBe('9999999999'); + expect([27, 28]).toContain(permit.v); + + const recovered = await recoverTypedDataAddress({ + domain: { name: 'Stable Coin', version: '1', chainId: 723487, verifyingContract: ASSET }, + types: EIP2612_TYPES, + primaryType: 'Permit', + message: { + owner: account.address, + spender: SPENDER, + value: 1000n, + nonce: 0n, + deadline: 9999999999n, + }, + signature: { r: permit.r, s: permit.s, v: BigInt(permit.v) }, + }); + expect(recovered.toLowerCase()).toBe(account.address.toLowerCase()); + }); +}); + +describe('encodePermitPaymentHeader', () => { + it('emits the flat radius-flavor envelope with a permit payload', async () => { + const account = privateKeyToAccount(PK); + const permit = await signPermit(account, { + asset: ASSET, + chainId: 723487, + name: 'Stable Coin', + version: '1', + spender: SPENDER, + value: 1000n, + nonce: 0n, + deadline: 9999999999n, + }); + const header = encodePermitPaymentHeader({ + x402Version: 2, + scheme: 'exact', + network: 'eip155:723487', + payload: permit, + }); + const decoded = JSON.parse(Buffer.from(header, 'base64').toString('utf8')); + expect(decoded.x402Version).toBe(2); + expect(decoded.scheme).toBe('exact'); + expect(decoded.network).toBe('eip155:723487'); + expect(decoded.payload.kind).toBe('permit-eip2612'); + expect(decoded.payload.owner).toBe(account.address); + expect(decoded.payload.spender).toBe(SPENDER); + expect(typeof decoded.payload.v).toBe('number'); + expect(decoded.payload.r).toMatch(/^0x[0-9a-f]{64}$/); + expect(decoded.payload.s).toMatch(/^0x[0-9a-f]{64}$/); + }); +}); diff --git a/tests/x402-permit2.test.ts b/tests/x402-permit2.test.ts new file mode 100644 index 0000000..81c8d11 --- /dev/null +++ b/tests/x402-permit2.test.ts @@ -0,0 +1,145 @@ +import { describe, it, expect } from 'vitest'; +import { recoverTypedDataAddress, type Address, type Hex } from 'viem'; +import { privateKeyToAccount } from 'viem/accounts'; +import { + buildPermit2PaymentPayload, + PERMIT2_ADDRESS, + PERMIT2_WITNESS_TYPES, + randomPermit2Nonce, + signPermit2WitnessTransfer, + X402_PERMIT2_PROXY, +} from '../src/lib/x402/permit2.js'; +import { signEip2612Permit } from '../src/lib/x402/eip2612.js'; + +const PK = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d' as Hex; +const SBC = '0x33ad9e4BD16B69B5BFdED37D8B5D9fF9aba014Fb' as Address; +const PAY_TO = '0x000000000000000000000000000000000000dEaD' as Address; + +describe('permit2 constants', () => { + it('uses the canonical Permit2 and x402 proxy addresses from the radius skill', () => { + expect(PERMIT2_ADDRESS).toBe('0x000000000022D473030F116dDEE9F6B43aC78BA3'); + expect(X402_PERMIT2_PROXY).toBe('0x402085c248EeA27D92E8b30b2C58ed07f9E20001'); + }); +}); + +describe('randomPermit2Nonce', () => { + it('produces distinct 128-bit nonces', () => { + const a = randomPermit2Nonce(); + const b = randomPermit2Nonce(); + expect(a).not.toBe(b); + expect(a).toBeLessThan(2n ** 128n); + expect(a).toBeGreaterThanOrEqual(0n); + }); +}); + +describe('signPermit2WitnessTransfer', () => { + it('signature recovers to the payer under the skill typed data (no domain version)', async () => { + const account = privateKeyToAccount(PK); + const nonce = 12345n; + const deadline = 9999999999n; + const signature = await signPermit2WitnessTransfer(account, { + token: SBC, + chainId: 723487, + amount: 1000n, + payTo: PAY_TO, + nonce, + deadline, + }); + const recovered = await recoverTypedDataAddress({ + domain: { name: 'Permit2', chainId: 723487, verifyingContract: PERMIT2_ADDRESS }, + types: PERMIT2_WITNESS_TYPES, + primaryType: 'PermitWitnessTransferFrom', + message: { + permitted: { token: SBC, amount: 1000n }, + spender: X402_PERMIT2_PROXY, + nonce, + deadline, + witness: { to: PAY_TO, validAfter: 0n }, + }, + signature, + }); + expect(recovered.toLowerCase()).toBe(account.address.toLowerCase()); + }); +}); + +describe('buildPermit2PaymentPayload', () => { + it('matches the radius x402 skill payload structure field-for-field', async () => { + const account = privateKeyToAccount(PK); + const deadline = 9999999999n; + const permit2Nonce = 777n; + const eip2612Nonce = 0n; + const eip2612Signature = await signEip2612Permit(account, { + asset: SBC, + chainId: 723487, + name: 'Stable Coin', + version: '1', + spender: PERMIT2_ADDRESS, + value: 1000n, + nonce: eip2612Nonce, + deadline, + }); + const permit2Signature = await signPermit2WitnessTransfer(account, { + token: SBC, + chainId: 723487, + amount: 1000n, + payTo: PAY_TO, + nonce: permit2Nonce, + deadline, + }); + const acceptedRaw = { + scheme: 'exact', + network: 'eip155:723487', + amount: '1000', + asset: SBC, + payTo: PAY_TO, + maxTimeoutSeconds: 300, + extra: { name: 'Stable Coin', version: '1', assetTransferMethod: 'permit2' }, + }; + const header = buildPermit2PaymentPayload({ + chainId: 723487, + resource: { url: 'https://example.com/api/data' }, + accepted: acceptedRaw, + token: SBC, + amount: 1000n, + owner: account.address, + payTo: PAY_TO, + permit2Signature, + permit2Nonce, + eip2612Signature, + eip2612Nonce, + deadline, + }); + const decoded = JSON.parse(Buffer.from(header, 'base64').toString('utf8')); + + expect(decoded.x402Version).toBe(2); + expect(decoded.scheme).toBe('exact'); + expect(decoded.network).toBe('eip155:723487'); + expect(decoded.resource).toEqual({ + url: 'https://example.com/api/data', + description: '', + mimeType: 'application/json', + }); + expect(decoded.accepted).toEqual(acceptedRaw); + + expect(decoded.payload.signature).toBe(permit2Signature); + expect(decoded.payload.permit2Authorization).toEqual({ + permitted: { token: SBC, amount: '1000' }, + from: account.address, + spender: X402_PERMIT2_PROXY, + nonce: '777', + deadline: '9999999999', + witness: { to: PAY_TO, validAfter: '0' }, + }); + + expect(decoded.extensions.eip2612GasSponsoring.info).toEqual({ + from: account.address, + asset: SBC, + spender: PERMIT2_ADDRESS, + amount: '1000', + nonce: '0', + deadline: '9999999999', + signature: eip2612Signature, + version: '1', + }); + }); +}); diff --git a/tests/x402-protocol.test.ts b/tests/x402-protocol.test.ts index 8b1ba13..70f18cf 100644 --- a/tests/x402-protocol.test.ts +++ b/tests/x402-protocol.test.ts @@ -36,6 +36,34 @@ describe('parseChallenge', () => { expect(c.error).toBe('X-PAYMENT required'); }); + it('falls back to `amount` when `maxAmountRequired` is absent (x402 v2)', () => { + const v2 = JSON.parse(JSON.stringify(VALID_CHALLENGE)); + delete v2.accepts[0].maxAmountRequired; + v2.accepts[0].amount = '13000'; + const c = parseChallenge(v2); + expect(c.accepts[0].maxAmountRequired).toBe(13000n); + }); + + it('prefers `maxAmountRequired` over `amount` when both are present', () => { + const both = JSON.parse(JSON.stringify(VALID_CHALLENGE)); + both.accepts[0].amount = '99'; + const c = parseChallenge(both); + expect(c.accepts[0].maxAmountRequired).toBe(13000n); + }); + + it('reports `amount` in the error when it is the source field', () => { + const bad = JSON.parse(JSON.stringify(VALID_CHALLENGE)); + delete bad.accepts[0].maxAmountRequired; + bad.accepts[0].amount = '-1'; + expect(() => parseChallenge(bad)).toThrow(/accepts\[0\]\.amount/); + }); + + it('rejects when neither maxAmountRequired nor amount is present', () => { + const neither = JSON.parse(JSON.stringify(VALID_CHALLENGE)); + delete neither.accepts[0].maxAmountRequired; + expect(() => parseChallenge(neither)).toThrow(/maxAmountRequired/); + }); + it('rejects non-objects', () => { expect(() => parseChallenge(null)).toThrow(); expect(() => parseChallenge([])).toThrow(); @@ -120,6 +148,79 @@ describe('encodePaymentHeader / decodePaymentResponse', () => { expect(decoded.payload.authorization.validBefore).toBe(1234567890); }); + it('emits the v2 envelope: accepted echo, resource, string validity bounds', () => { + const v2Challenge = { + x402Version: 2, + accepts: [ + { + scheme: 'exact', + network: 'eip155:723487', + amount: '1000', + asset: '0x33ad9e4BD16B69B5BFdED37D8B5D9fF9aba014Fb', + payTo: '0x000000000000000000000000000000000000dEaD', + maxTimeoutSeconds: 300, + extra: { name: 'Stable Coin', version: '1' }, + }, + ], + resource: { url: 'https://example.com/r', method: 'GET' }, + error: 'Payment required', + }; + const c = parseChallenge(v2Challenge); + const header = encodePaymentHeader({ + x402Version: c.x402Version, + scheme: c.accepts[0].scheme, + network: c.accepts[0].network, + accepted: c.accepts[0].raw, + resource: c.resource, + payload: { + signature: '0xdeadbeef', + authorization: { + from: '0x0000000000000000000000000000000000000001', + to: '0x000000000000000000000000000000000000dEaD', + value: 1000n, + validAfter: 0, + validBefore: 1234567890, + nonce: '0xaa', + }, + }, + }); + const decoded = JSON.parse(Buffer.from(header, 'base64').toString('utf8')); + expect(decoded.x402Version).toBe(2); + // v2: accepted echoes the server's entry verbatim (amount, not maxAmountRequired) + expect(decoded.accepted).toEqual(v2Challenge.accepts[0]); + expect(decoded.resource).toEqual(v2Challenge.resource); + // v2: flat scheme/network kept alongside the accepted echo (radius x402 skill) + expect(decoded.scheme).toBe('exact'); + expect(decoded.network).toBe('eip155:723487'); + // v2: validity bounds are strings + expect(decoded.payload.authorization.validAfter).toBe('0'); + expect(decoded.payload.authorization.validBefore).toBe('1234567890'); + expect(decoded.payload.authorization.value).toBe('1000'); + }); + + it('keeps the v1 envelope unchanged for x402Version 1', () => { + const header = encodePaymentHeader({ + x402Version: 1, + scheme: 'exact', + network: 'eip155:723487', + payload: { + signature: '0xdeadbeef', + authorization: { + from: '0x0000000000000000000000000000000000000001', + to: '0x000000000000000000000000000000000000dEaD', + value: 1000n, + validAfter: 0, + validBefore: 1234567890, + nonce: '0xaa', + }, + }, + }); + const decoded = JSON.parse(Buffer.from(header, 'base64').toString('utf8')); + expect(decoded.scheme).toBe('exact'); + expect(decoded.accepted).toBeUndefined(); + expect(decoded.payload.authorization.validBefore).toBe(1234567890); + }); + it('decodes a payment-response header', () => { const body = { success: true,