From 51d674eec4eed38176a550911ce9dd13fd97296a Mon Sep 17 00:00:00 2001 From: Caleb! <48127194+kaf-lamed-beyt@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:04:03 +0100 Subject: [PATCH 1/3] feat: pinata migration (#9) storacha is shutting down on april 15 (uploads) and may 31 (gateways). this migrates the storage backend to pinata as announced in #8. files are packed into CAR format via ipfs-car and pinned to ipfs through pinata. - add pinata.service.ts with `pinCAR`, `unpinCID`, `getUsage`, `gatewayUrl` - add buildCAR to compute-cid.ts; `computeCID` now delegates to it - update upload, agent, jobs, console controllers to use pinata --- .DS_Store | Bin 6148 -> 0 bytes pnpm-lock.yaml | 856 ++++-------------- server/package.json | 3 +- server/src/controllers/agent.controller.ts | 40 +- server/src/controllers/console.controller.ts | 42 +- server/src/controllers/jobs.controller.ts | 26 +- server/src/controllers/pricing.controller.ts | 2 +- server/src/controllers/storage.controller.ts | 2 +- server/src/controllers/upload.controller.ts | 123 ++- server/src/controllers/user.controller.ts | 54 -- server/src/index.ts | 4 +- server/src/middlewares/x402.middleware.ts | 2 +- server/src/routes/user.route.ts | 6 - server/src/services/storage/pinata.service.ts | 68 ++ server/src/services/usage/usage.service.ts | 194 +--- .../src/tests/User/createDelegation.test.ts | 27 - server/src/utils/compute-cid.ts | 127 ++- server/src/utils/{storacha.ts => pricing.ts} | 32 - 18 files changed, 453 insertions(+), 1155 deletions(-) delete mode 100644 .DS_Store delete mode 100644 server/src/controllers/user.controller.ts delete mode 100644 server/src/routes/user.route.ts create mode 100644 server/src/services/storage/pinata.service.ts delete mode 100644 server/src/tests/User/createDelegation.test.ts rename server/src/utils/{storacha.ts => pricing.ts} (61%) diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index e40e18bc1914b3cdae6a4893e9eff4171b7682af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKy-veG4EB`>1tCO7#v55c^bOJyo}e2O{gDD9;UWqnQU*pQo`DD8ExI!?@(PS> ze7>{VL;=c#0NIlL-Pz~d-Ipq^iO3ADr#+%B5yeo(&IZf}!g1D?bi~6!!_UaG-f-fI zvb9njzm)-gcM2IWM?BCit)SS`PMHdlLS<&3@8Il2G+u~%k}^0 z^Zvh?q%&ne8TeNWm?#~l16)aJYwL1cYc2Ey%EIfafQu9id@06SF2y@gFYsF)029F~ RAUqKLBj9OJqYV5g1Mdk_S;PPU diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eeec31e..6ab5c69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 2.4.9 '@changesets/cli': specifier: ^2.30.0 - version: 2.30.0(@types/node@25.5.0) + version: 2.30.0(@types/node@25.5.2) husky: specifier: ^9.1.7 version: 9.1.7 @@ -32,7 +32,7 @@ importers: devDependencies: knip: specifier: ^5.88.1 - version: 5.88.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)(typescript@5.9.3) + version: 5.88.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(typescript@5.9.3) tsup: specifier: ^8.5.1 version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) @@ -44,14 +44,14 @@ importers: dependencies: '@solana/kit': specifier: ^2.3.0 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/web3.js': specifier: ^1.98.4 - version: 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6) + version: 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10) devDependencies: knip: specifier: ^5.88.1 - version: 5.88.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)(typescript@5.9.3) + version: 5.88.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(typescript@5.9.3) tsup: specifier: ^8.5.1 version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) @@ -63,13 +63,13 @@ importers: dependencies: '@x402/core': specifier: ^2.8.0 - version: 2.8.0 + version: 2.9.0 '@x402/evm': specifier: ^2.8.0 - version: 2.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) + version: 2.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) '@x402/fetch': specifier: ^2.8.0 - version: 2.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) + version: 2.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) viem: specifier: ~2.45.3 version: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) @@ -85,10 +85,10 @@ importers: dependencies: '@coinbase/x402': specifier: ^2.1.0 - version: 2.1.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + version: 2.1.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6) '@coral-xyz/anchor': specifier: ^0.31.1 - version: 0.31.1(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10) + version: 0.31.1(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6) '@logtail/node': specifier: ^0.5.8 version: 0.5.8 @@ -106,28 +106,22 @@ importers: version: 10.46.0 '@solana/web3.js': specifier: ^1.98.4 - version: 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@storacha/client': - specifier: ^2.1.3 - version: 2.1.3(encoding@0.1.13) + version: 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6) '@types/bn.js': specifier: ^5.2.0 version: 5.2.0 - '@ucanto/core': - specifier: ^10.4.6 - version: 10.4.6 '@upstash/qstash': specifier: ^2.10.1 version: 2.10.1 '@x402/core': specifier: ^2.8.0 - version: 2.8.0 + version: 2.9.0 '@x402/evm': specifier: ^2.8.0 - version: 2.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + version: 2.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) '@x402/express': specifier: ^2.8.0 - version: 2.8.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(express@5.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10) + version: 2.9.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(express@5.2.1)(typescript@5.9.3)(utf-8-validate@6.0.6) axios: specifier: ^1.14.0 version: 1.14.0 @@ -139,7 +133,7 @@ importers: version: 2.8.6 dotenv: specifier: ^17.3.1 - version: 17.3.1 + version: 17.4.0 drizzle-orm: specifier: ^0.44.7 version: 0.44.7(@neondatabase/serverless@1.0.2)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.20.0)(postgres@3.4.8) @@ -167,6 +161,9 @@ importers: pg: specifier: ^8.20.0 version: 8.20.0 + pinata: + specifier: 2.5.5 + version: 2.5.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) postgres: specifier: ^3.4.8 version: 3.4.8 @@ -360,19 +357,19 @@ importers: dependencies: '@x402/core': specifier: latest - version: 2.8.0 + version: 2.9.0 '@x402/evm': specifier: latest - version: 2.8.0(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6) + version: 2.9.0(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6) '@x402/express': specifier: latest - version: 2.8.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(express@5.2.1)(typescript@6.0.2)(utf-8-validate@6.0.6) + version: 2.9.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(express@5.2.1)(typescript@6.0.2)(utf-8-validate@6.0.6) '@x402/fetch': specifier: latest - version: 2.8.0(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6) + version: 2.9.0(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6) dotenv: specifier: latest - version: 17.3.1 + version: 17.4.0 express: specifier: latest version: 5.2.1 @@ -385,7 +382,7 @@ importers: version: 5.0.6 '@types/node': specifier: latest - version: 25.5.0 + version: 25.5.2 tsx: specifier: latest version: 4.21.0 @@ -1447,9 +1444,6 @@ packages: resolution: {integrity: sha512-w4PZ2yPqvNmlAir7/2hsCRMqny1EY5jj26iZcSgxREJexmbAc2FI21jp26MqiNdfgAxvkCnf2N/TJI18GaDNwA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} - '@ipld/dag-ucan@3.4.5': - resolution: {integrity: sha512-wiWhH0Ju7WgnumsarHCYtlo+1NRy+WIsXyAB7g2sDqVsKCyEJiLLmjeZzKqNv+qMxLnUS8bQ287cPiQ//s/oJQ==} - '@ipld/unixfs@3.0.0': resolution: {integrity: sha512-Tj3/BPOlnemcZQ2ETIZAO8hqAs9KNzWyX5J9+JCL9jDwvYwjxeYjqJ3v+9DusNvTBmJhZnGVP6ijUHrsuOLp+g==} @@ -1722,9 +1716,6 @@ packages: resolution: {integrity: sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==} engines: {node: '>= 20.19.0'} - '@noble/ed25519@1.7.5': - resolution: {integrity: sha512-xuS0nwRMQBvSxDa7UxMb61xTiH3MxTgUfhyPUALVIe0FlOAz4sjELwyDRyUvqeEYfRSG9qNjFIycqLZppg4RSA==} - '@noble/hashes@1.3.2': resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} engines: {node: '>= 16'} @@ -3579,34 +3570,6 @@ packages: resolution: {integrity: sha512-7nh2ogzLRMhfkIC0fGjn1LHUzk3jqVw8tjAuTt5ADWfL9CSGBL18ILucE9igz2L/RU2AZgeAvhujAnW91Ut/oQ==} engines: {node: '>=20.0.0'} - '@storacha/access@1.6.9': - resolution: {integrity: sha512-hdEyVg6gvYmYbKN1K3h+U7m/BbTwxnegMfi9ZJm/7D7Vk3kuttURv49DmsmfH/LPHCqAiP1+pEcFUzkZv4oAKg==} - - '@storacha/blob-index@1.2.9': - resolution: {integrity: sha512-6tsnh4c/bTNJGKw4uuXZGTVYcxy19jdsK/DHE4aZajPha96bW9RnZTxLO1e0VIXPWNBXo/Hvxb0nik+OfTQHkw==} - engines: {node: '>=16.15'} - hasBin: true - - '@storacha/capabilities@2.3.1': - resolution: {integrity: sha512-cXSdRdqZoyKpIAHMvr+rqJpS+xdJ/n8e3w7CIbswuaHVo+vj54PeGOLm3YuQA+K7AX1GEn2mPVmW1V192NmBwg==} - - '@storacha/client@2.1.3': - resolution: {integrity: sha512-TpIzSPZpz3WpYzeRhVgm/WhrFwLZdvy2jrU3p27S5mf4ZP6kpMfbUQI02f8a2eIRzjdT4e6YuQ1qn3wbQsNj9Q==} - engines: {node: '>=18'} - - '@storacha/did-mailto@1.0.2': - resolution: {integrity: sha512-0ee1t15yG4xdyIYz2iL+wP06VnGQf8zJ/xpszjf4sdP2sTgAVzx92LfEgHcuIbFF4sabcJXzHFAcMi8l8u64Mg==} - engines: {node: '>=16.15'} - - '@storacha/filecoin-client@1.0.20': - resolution: {integrity: sha512-vcc2490ji1mveErErPFKYuX0gf0D+RbloxiArd3v0uPkPfgO/xxZG/gtMQeq2EwAbHD2BRVD+X4lxHUOlVoDZg==} - - '@storacha/one-webcrypto@1.0.1': - resolution: {integrity: sha512-bD+vWmcgsEBqU0Dz04BR43SA03bBoLTAY29vaKasY9Oe8cb6XIP0/vkm0OS2UwKC13c8uRgFW4rjJUgDCNLejQ==} - - '@storacha/upload-client@1.4.2': - resolution: {integrity: sha512-vXn6ZMa39DXOJL0IbnxOfLDPHzA7CV1zmGk3mXr4bHhvAJekgWiZq+KluiMt9AfumnpPI7Nknrs0ZmUnwzrOYQ==} - '@stylistic/eslint-plugin@5.10.0': resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4024,9 +3987,6 @@ packages: '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - '@types/minimatch@3.0.5': - resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} - '@types/morgan@1.9.10': resolution: {integrity: sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==} @@ -4051,8 +4011,8 @@ packages: '@types/node@24.12.0': resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==} - '@types/node@25.5.0': - resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} + '@types/node@25.5.2': + resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -4080,9 +4040,6 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} - '@types/retry@0.12.1': - resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} - '@types/send@1.2.1': resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} @@ -4190,24 +4147,6 @@ packages: resolution: {integrity: sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ucanto/client@9.0.2': - resolution: {integrity: sha512-XbG8rw/fc1hPSFKDQ/z1NpIuc4Lm79J4NWbP+dedvDnb5LmOBMHE8bjjEPD4xGO2eh6fWQSZ4YTYk5wjkLz+3Q==} - - '@ucanto/core@10.4.6': - resolution: {integrity: sha512-K3aRsbolL3Mas1P+7Ba+kpCvEVlBJf93V4OZ23FOZRBnbodD+x/ygOEXWL8OYcZpBwYiPeYWMAJPZpIzjNIEoQ==} - - '@ucanto/interface@11.0.1': - resolution: {integrity: sha512-d0yoPFXdbb3EyM3sBom5VVcVyz58HT+57qi/ywSY1dbZAm4c7GZss0lpfcI5OXQREzGCzJYAg1e7pFwqbATnmQ==} - - '@ucanto/principal@9.0.3': - resolution: {integrity: sha512-MFLvckOpQA46VSeLWyB7xCysL+ub5vnbCEtHbWhNE3qkzeo14v4wSThd60odPhxj83QXrzbveiebp0gcaEvXJg==} - - '@ucanto/transport@9.2.1': - resolution: {integrity: sha512-/Dhr+2vjpGO5GTKsSl8XcPQFtFdZvIA9Yk/yzzY9DHFsdI+tXrUjLWYwXcidx8EIdx5Xd9oitF4wodIZuiW8GQ==} - - '@ucanto/validator@10.0.1': - resolution: {integrity: sha512-J1/2NAfvs55CxDEuHKMTjoLgoVrFqBbZqErYwL7TDx8Pu2V9sv7rc2wZS5dRxcM1VCEzHXY3SWeLOJuH8VgKrw==} - '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -4513,29 +4452,26 @@ packages: '@web3-storage/car-block-validator@1.2.2': resolution: {integrity: sha512-lR9l+ZszhTid5HfZE8ohnGf2RJp2kaBOnoejmsACs3iTNiy+3K09dnPm8MhgBE9RCIgPBKM0CCWXO9l+I6jrKA==} - '@web3-storage/data-segment@5.3.0': - resolution: {integrity: sha512-zFJ4m+pEKqtKatJNsFrk/2lHeFSbkXZ6KKXjBe7/2ayA9wAar7T/unewnOcZrrZTnCWmaxKsXWqdMFy9bXK9dw==} + '@x402/core@2.9.0': + resolution: {integrity: sha512-IqPITHYx6XHlgLPtparuKKwoB+3wQdgt0F+WUH1e3WHMeiWdp+xTtQDy+6yOKuObNFI1S1iVbQFz0GivR/Vv3w==} - '@x402/core@2.8.0': - resolution: {integrity: sha512-ppsvSzyWzlqG+26dcNzOXo/YLaHreWc3lmdv9W81mEhLDWMdPpiGyRjSUyO9BQFuQJMQppvqA7ujUbLE/EaDkg==} + '@x402/evm@2.9.0': + resolution: {integrity: sha512-qUhnKe1pym9a+7dzeK+6ripsddVsr+5PNcpQfTYK4dubW+1SR9MRx/O4PNRtedWoAxminqAwmCL5AQUiSVvKWA==} - '@x402/evm@2.8.0': - resolution: {integrity: sha512-/w1WvoXIZdaJcmvR8p9/ow1n8GjNWeeVnpoorjfLv2oBbT06r33clcfYwhH/COJp/K0aOvmol0elx5h2IQCnOA==} - - '@x402/express@2.8.0': - resolution: {integrity: sha512-N+lQWuwGKRWnpXVICkoHA8IncO6cfs/ZEzdhW839vOoFMTj4JiqyL6MUw4WMpmpJ4D+ZFe1eCTnYY5A7iaMB7A==} + '@x402/express@2.9.0': + resolution: {integrity: sha512-E236c188p7rkAILnzdd19FQZuTEOM9+DP/y/FG4B0oawiBXx5FGR5ez7EUKCG7V7FxXsGvMEjUFMJ0BjrQx8yw==} peerDependencies: - '@x402/paywall': ^2.8.0 + '@x402/paywall': ^2.9.0 express: ^4.0.0 || ^5.0.0 peerDependenciesMeta: '@x402/paywall': optional: true - '@x402/extensions@2.8.0': - resolution: {integrity: sha512-3VHY7cjyYkI8JMbF81Soh9gEcFFekXiGr+71Jp39lOzIGmX67Q8+2hkLiIu5laKoaz0vi2gnaqTYESADRhO9DA==} + '@x402/extensions@2.9.0': + resolution: {integrity: sha512-Fk7MJFWSQtsRGGZcNpgwv8V6IG38USNciA5ImkF0InceWDfjMBq6b8GmksM+cVHAXQsMQYxw3HV8uhfwTPEGAQ==} - '@x402/fetch@2.8.0': - resolution: {integrity: sha512-GEY6DS5iy/PxwfbdfIBY0RsxrHnHylKmXMqzL8BJdSaerm3w5XhTkNCOG6JyaIt1CpQccYXbzlOEpqFJQNaNAA==} + '@x402/fetch@2.9.0': + resolution: {integrity: sha512-/ocFWG5aTftCabW4CWXLZjafzTwkmW+c7u4lzXLegJ7F8Fkciw6yyzrQ+R4Rsm6F6TH0pGzo545Yc7n7z5UNlA==} '@xrplf/isomorphic@1.0.1': resolution: {integrity: sha512-0bIpgx8PDjYdrLFeC3csF305QQ1L7sxaWnL5y71mCvhenZzJgku9QsA+9QCXBC1eNYtxWO/xR91zrXJy2T/ixg==} @@ -4632,14 +4568,6 @@ packages: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} @@ -4688,9 +4616,6 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - any-signal@3.0.1: - resolution: {integrity: sha512-xgZgJtKEa9YmDqXodIgl7Fl1C8yNXr8w6gXjqK3LW4GcEiYT+6AQfJSE/8SPsEpLLmcvbv8YU+qet94UewHxqg==} - anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -4742,9 +4667,6 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} - atomically@2.1.1: - resolution: {integrity: sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==} - attr-accept@2.2.5: resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==} engines: {node: '>=4'} @@ -4872,10 +4794,6 @@ packages: big.js@6.2.2: resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} - bigint-mod-arith@3.3.1: - resolution: {integrity: sha512-pX/cYW3dCa87Jrzv6DAr8ivbbJRzEX5yGhdt8IutnX/PCIXfpx+mabWNK/M8qqh+zQ0J3thftUBHW0ByuUlG0w==} - engines: {node: '>=10.4.0'} - bignumber.js@9.3.1: resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} @@ -4932,9 +4850,6 @@ packages: brorand@1.1.0: resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - browser-readablestream-to-it@1.0.3: - resolution: {integrity: sha512-+12sHB+Br8HIh6VAMVEG5r3UXCyESIgDW7kzk3BjIXa43DVqVwL7GC5TW3jeh+72dtcH99pPVpw0X8i0jt+/kw==} - browser-resolve@2.0.0: resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} @@ -5056,9 +4971,6 @@ packages: caniuse-lite@1.0.30001781: resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} - carstream@2.3.0: - resolution: {integrity: sha512-2YwFg5Kxs2tqVCJv7sthWbYoUpALCYBBfTdpQcpicV7ipi6bBb1h9M4MNb1vm+724f39lUNp5VWhW43IFxfPlA==} - cashaddrjs@0.4.4: resolution: {integrity: sha512-xZkuWdNOh0uq/mxJIng6vYWfTowZLd9F4GMAlp2DwFHlcCqCm91NtuAc47RuV4L7r4PYcY5p6Cr2OKNb4hnkWA==} @@ -5243,10 +5155,6 @@ packages: resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} engines: {'0': node >= 6.0} - conf@11.0.2: - resolution: {integrity: sha512-jjyhlQ0ew/iwmtwsS2RaB6s8DBifcE2GYBEaw2SJDUY/slJJbNfY4GlDVzOs/ff8cM/Wua5CikqXgbFl5eu85A==} - engines: {node: '>=14.16'} - confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} @@ -5372,10 +5280,6 @@ packages: dayjs@1.11.20: resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} - debounce-fn@5.1.2: - resolution: {integrity: sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==} - engines: {node: '>=12'} - debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -5510,12 +5414,8 @@ packages: resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} engines: {node: '>=10'} - dot-prop@7.2.0: - resolution: {integrity: sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - dotenv@17.3.1: - resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} + dotenv@17.4.0: + resolution: {integrity: sha512-kCKF62fwtzwYm0IGBNjRUjtJgMfGapII+FslMHIjMR5KTnwEmBmWLDRSnc3XSNP8bNy34tekgQyDT0hr7pERRQ==} engines: {node: '>=12'} draggabilly@3.0.0: @@ -5633,10 +5533,6 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-fetch@1.9.1: - resolution: {integrity: sha512-M9qw6oUILGVrcENMSRRefE1MbHPIz0h79EKIeJWK9v563aT9Qkh8aEHPO1H5vi970wPirNY+jO9OpFoLiMsMGA==} - engines: {node: '>=6'} - electron-to-chromium@1.5.328: resolution: {integrity: sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==} @@ -5705,17 +5601,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - env-paths@3.0.0: - resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} - err-code@3.0.1: - resolution: {integrity: sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA==} - error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -5987,9 +5876,6 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -6209,9 +6095,6 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-iterator@1.0.2: - resolution: {integrity: sha512-v+dm9bNVfOYsY1OrhaCrmyOcYoSeVvbt+hHZ0Au+T+p1y+0Uyj9aMaGIeUTT6xdpRbWzDeYKvfOslPhggQMcsg==} - get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} @@ -6489,10 +6372,6 @@ packages: ipfs-unixfs@11.2.5: resolution: {integrity: sha512-uasYJ0GLPbViaTFsOLnL9YPjX5VmhnqtWRriogAHOe4ApmIi9VAOFBzgDHsUW2ub4pEa/EysbtWk126g2vkU/g==} - ipfs-utils@9.0.14: - resolution: {integrity: sha512-zIaiEGX18QATxgaS0/EOQNoo33W0islREABAcxXE8n7y2MGAlB+hdsxXn4J0hGZge8IqVQhW8sWIb+oJz2yEvg==} - engines: {node: '>=16.0.0', npm: '>=7.0.0'} - iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -6535,9 +6414,6 @@ packages: engines: {node: '>=8'} hasBin: true - is-electron@2.2.2: - resolution: {integrity: sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -6635,10 +6511,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - iso-url@1.2.1: - resolution: {integrity: sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng==} - engines: {node: '>=12'} - isomorphic-timers-promises@1.0.1: resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} engines: {node: '>=10'} @@ -6682,15 +6554,9 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} - it-all@1.0.6: - resolution: {integrity: sha512-3cmCc6Heqe3uWi3CVM/k51fa/XbMFpQVzFoDsV0IZNHSQDyAXl3c4MjHkFX5kF3922OGj7Myv1nSEUgRtcuM1A==} - it-filter@3.1.4: resolution: {integrity: sha512-80kWEKgiFEa4fEYD3mwf2uygo1dTQ5Y5midKtL89iXyjinruA/sNXl6iFkTcdNedydjvIsFhWLiqRPQP4fAwWQ==} - it-glob@1.0.2: - resolution: {integrity: sha512-Ch2Dzhw4URfB9L/0ZHyY+uqOnKvBNeS/SMcRiPmJfpHiM0TsUZn+GkpcZxAoF3dJVdPm/PuIk3A4wlV7SUo23Q==} - it-last@3.0.9: resolution: {integrity: sha512-AtfUEnGDBHBEwa1LjrpGHsJMzJAWDipD6zilvhakzJcm+BCvNX8zlX2BsHClHJLLTrsY4lY9JUjc+TQV4W7m1w==} @@ -6719,9 +6585,6 @@ packages: it-stream-types@2.0.2: resolution: {integrity: sha512-Rz/DEZ6Byn/r9+/SBCuJhpPATDF9D+dz5pbgSUyBsCDtza6wtNATrz/jz1gDyNanC3XdLboriHnOC925bZRBww==} - it-to-stream@1.0.0: - resolution: {integrity: sha512-pLULMZMAB/+vbdvbZtebC0nWBTbG581lk6w8P7DfIIIKUfa8FbY7Oi0FxZcFPbxvISs7A9E+cMpLDBc1XhpAOA==} - jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -6963,9 +6826,6 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -7357,10 +7217,6 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} @@ -7458,11 +7314,6 @@ packages: engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} hasBin: true - native-fetch@3.0.0: - resolution: {integrity: sha512-G3Z7vx0IFb/FQ4JxvtqGABsOTIqRWvgQz6e+erkB+JJD6LrszQtMozEHI4EkmgZQvnGHrpLVzUWk7t4sJCIkVw==} - peerDependencies: - node-fetch: '*' - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -7598,9 +7449,6 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - one-webcrypto@1.0.3: - resolution: {integrity: sha512-fu9ywBVBPx0gS9K0etIROTiCkvI5S1TDjFsYFb3rC1ewFxeOqsbzq7aIMBHsYfrTHBcGXJaONXXjTl8B01cW1Q==} - onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -7650,17 +7498,10 @@ packages: oxc-resolver@11.19.1: resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} - p-defer@3.0.0: - resolution: {integrity: sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==} - engines: {node: '>=8'} - p-defer@4.0.1: resolution: {integrity: sha512-Mr5KC5efvAK5VUptYEIopP1bakB85k2IWXaRC0rsh1uwn1L6M0LVml8OIQ4Gudg4oyZakf7FmeRLkMMtZW1i5A==} engines: {node: '>=12'} - p-fifo@1.0.0: - resolution: {integrity: sha512-IjoCxXW48tqdtDFz6fqo5q1UfFVjjVZe8TC1QRflvNUJtNfCUhxOUw6MOVZhDPjqhSzc26xKdugsO17gmzd5+A==} - p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -7689,10 +7530,6 @@ packages: resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==} engines: {node: '>=18'} - p-retry@5.1.2: - resolution: {integrity: sha512-couX95waDu98NfNZV+i/iLt+fdVxmI7CbrrdC2uDWfPdUAApyxT4wmDlyOtR5KtTDmkDO0zDScDjDou9YHhd9g==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-timeout@6.1.4: resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} engines: {node: '>=14.16'} @@ -7824,6 +7661,18 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + pinata@2.5.5: + resolution: {integrity: sha512-GmHGL8bSpiwQlbrXrT73DJ1ZrYi+MD3lY7scY10D2PnH0kggDCUP04Gb96thbRenhvqVYjPRYYFt7gW9yjejYw==} + engines: {node: '>=20'} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + pino-abstract-transport@0.5.0: resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==} @@ -8107,9 +7956,6 @@ packages: react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 - react-native-fetch-api@3.0.0: - resolution: {integrity: sha512-g2rtqPjdroaboDKTsJCTlcmtw54E25OjyaunUP0anOZn4Fuo2IKs8BVfe02zVggA/UysbmfSnRJIqtNkAgggNA==} - react-native@0.83.1: resolution: {integrity: sha512-mL1q5HPq5cWseVhWRLl+Fwvi5z1UO+3vGOpjr+sHFwcUletPRZ5Kv+d0tUfqHmvi73/53NjlQqX1Pyn4GguUfA==} engines: {node: '>= 20.19.4'} @@ -8261,10 +8107,6 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -8616,9 +8458,6 @@ packages: stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} - stream-to-it@0.2.4: - resolution: {integrity: sha512-4vEbkSs83OahpmBybNJXlJd7d6/RxzkkSdT3I0mnGt79Xd2Kk+e1JqbvAvsQfCeKj3aKb0QIWkyK3/n0j506vQ==} - streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -8691,12 +8530,6 @@ packages: strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - stubborn-fs@2.0.0: - resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} - - stubborn-utils@1.0.2: - resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} - style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -8745,9 +8578,6 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - sync-multihash-sha2@1.0.0: - resolution: {integrity: sha512-A5gVpmtKF0ov+/XID0M0QRJqF2QxAsj3x/LlDC8yivzgoYCoWkV+XaZPfVu7Vj1T/hYzYS1tfjwboSbXjqocug==} - synckit@0.11.12: resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -8981,10 +8811,6 @@ packages: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} - type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} @@ -9487,9 +9313,6 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - when-exit@2.1.5: - resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} - which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} @@ -10044,7 +9867,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.30.0(@types/node@25.5.0)': + '@changesets/cli@2.30.0(@types/node@25.5.2)': dependencies: '@changesets/apply-release-plan': 7.1.0 '@changesets/assemble-release-plan': 6.0.9 @@ -10060,7 +9883,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@25.5.0) + '@inquirer/external-editor': 1.0.3(@types/node@25.5.2) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 enquirer: 2.4.1 @@ -10158,19 +9981,19 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 - '@coinbase/cdp-sdk@1.45.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@coinbase/cdp-sdk@1.45.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: - '@solana-program/system': 0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@solana-program/token': 0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana-program/system': 0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6)) + '@solana-program/token': 0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6)) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6) abitype: 1.0.6(typescript@5.9.3)(zod@3.25.76) axios: 1.14.0 axios-retry: 4.5.0(axios@1.14.0) jose: 6.2.2 md5: 2.3.0 uncrypto: 0.1.3 - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -10180,11 +10003,11 @@ snapshots: - typescript - utf-8-validate - '@coinbase/x402@2.1.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@coinbase/x402@2.1.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: - '@coinbase/cdp-sdk': 1.45.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@x402/core': 2.8.0 - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@coinbase/cdp-sdk': 1.45.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6) + '@x402/core': 2.9.0 + viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -10196,12 +10019,12 @@ snapshots: '@coral-xyz/anchor-errors@0.31.1': {} - '@coral-xyz/anchor@0.31.1(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@coral-xyz/anchor@0.31.1(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: '@coral-xyz/anchor-errors': 0.31.1 - '@coral-xyz/borsh': 0.31.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@coral-xyz/borsh': 0.31.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6)) '@noble/hashes': 1.8.0 - '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6) bn.js: 5.2.3 bs58: 4.0.1 buffer-layout: 1.2.2 @@ -10217,9 +10040,9 @@ snapshots: - typescript - utf-8-validate - '@coral-xyz/borsh@0.31.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10))': + '@coral-xyz/borsh@0.31.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6))': dependencies: - '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6) bn.js: 5.2.3 buffer-layout: 1.2.2 @@ -10703,12 +10526,12 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/external-editor@1.0.3(@types/node@25.5.0)': + '@inquirer/external-editor@1.0.3(@types/node@25.5.2)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 '@ipld/car@5.4.2': dependencies: @@ -10731,12 +10554,6 @@ snapshots: dependencies: multiformats: 13.4.2 - '@ipld/dag-ucan@3.4.5': - dependencies: - '@ipld/dag-cbor': 9.2.5 - '@ipld/dag-json': 10.2.6 - multiformats: 13.4.2 - '@ipld/unixfs@3.0.0': dependencies: '@ipld/dag-pb': 4.1.5 @@ -10822,7 +10639,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.19.15 + '@types/node': 24.12.0 jest-mock: 29.7.0 '@jest/environment@30.3.0': @@ -10847,7 +10664,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.19.15 + '@types/node': 24.12.0 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -11215,8 +11032,6 @@ snapshots: dependencies: '@noble/hashes': 2.0.1 - '@noble/ed25519@1.7.5': {} - '@noble/hashes@1.3.2': {} '@noble/hashes@1.4.0': {} @@ -11800,7 +11615,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) transitivePeerDependencies: - bufferutil - typescript @@ -11811,7 +11626,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript @@ -11824,7 +11639,7 @@ snapshots: '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(utf-8-validate@5.0.10)))(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) - viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11938,7 +11753,7 @@ snapshots: '@walletconnect/logger': 2.1.2 '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(utf-8-validate@5.0.10)))(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) - viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11991,7 +11806,7 @@ snapshots: '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(utf-8-validate@5.0.10)))(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) - viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12317,9 +12132,9 @@ snapshots: dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))': + '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6))': dependencies: - '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6) '@solana-program/system@0.7.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)))': dependencies: @@ -12334,9 +12149,9 @@ snapshots: dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))': + '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6))': dependencies: - '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6) '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: @@ -12583,7 +12398,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12596,11 +12411,11 @@ snapshots: '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) typescript: 5.9.3 @@ -12608,7 +12423,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12621,11 +12436,11 @@ snapshots: '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) typescript: 5.9.3 @@ -12633,7 +12448,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: '@solana/accounts': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12650,11 +12465,11 @@ snapshots: '@solana/rpc-api': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) - '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6) '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/signers': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/sysvars': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/transaction-confirmation': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6) '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: @@ -12830,31 +12645,31 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.3) '@solana/functional': 2.3.0(typescript@5.9.3) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) '@solana/subscribable': 2.3.0(typescript@5.9.3) typescript: 5.9.3 - ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6) + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.3) '@solana/functional': 2.3.0(typescript@5.9.3) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) '@solana/subscribable': 2.3.0(typescript@5.9.3) typescript: 5.9.3 - ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: '@solana/errors': 5.5.1(typescript@5.9.3) '@solana/functional': 5.5.1(typescript@5.9.3) '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) '@solana/subscribable': 5.5.1(typescript@5.9.3) - ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -12878,7 +12693,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.3) '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.3) @@ -12886,7 +12701,7 @@ snapshots: '@solana/promises': 2.3.0(typescript@5.9.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12896,7 +12711,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.3) '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.3) @@ -12904,7 +12719,7 @@ snapshots: '@solana/promises': 2.3.0(typescript@5.9.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12914,7 +12729,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/rpc-subscriptions@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: '@solana/errors': 5.5.1(typescript@5.9.3) '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) @@ -12922,7 +12737,7 @@ snapshots: '@solana/promises': 5.5.1(typescript@5.9.3) '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) '@solana/rpc-subscriptions-api': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions-channel-websocket': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) '@solana/rpc-transformers': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -13092,7 +12907,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -13100,7 +12915,7 @@ snapshots: '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/promises': 2.3.0(typescript@5.9.3) '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -13109,7 +12924,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -13117,7 +12932,7 @@ snapshots: '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/promises': 2.3.0(typescript@5.9.3) '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -13126,7 +12941,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/transaction-confirmation@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -13134,7 +12949,7 @@ snapshots: '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/promises': 5.5.1(typescript@5.9.3) '@solana/rpc': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@6.0.6) '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -13786,101 +13601,6 @@ snapshots: transitivePeerDependencies: - debug - '@storacha/access@1.6.9': - dependencies: - '@ipld/car': 5.4.2 - '@ipld/dag-ucan': 3.4.5 - '@scure/bip39': 1.6.0 - '@storacha/capabilities': 2.3.1 - '@storacha/did-mailto': 1.0.2 - '@storacha/one-webcrypto': 1.0.1 - '@ucanto/client': 9.0.2 - '@ucanto/core': 10.4.6 - '@ucanto/interface': 11.0.1 - '@ucanto/principal': 9.0.3 - '@ucanto/transport': 9.2.1 - '@ucanto/validator': 10.0.1 - bigint-mod-arith: 3.3.1 - conf: 11.0.2 - multiformats: 13.4.2 - p-defer: 4.0.1 - type-fest: 4.41.0 - uint8arrays: 5.1.0 - - '@storacha/blob-index@1.2.9': - dependencies: - '@ipld/dag-cbor': 9.2.5 - '@storacha/capabilities': 2.3.1 - '@storacha/one-webcrypto': 1.0.1 - '@ucanto/core': 10.4.6 - '@ucanto/interface': 11.0.1 - carstream: 2.3.0 - multiformats: 13.4.2 - sade: 1.8.1 - uint8arrays: 5.1.0 - - '@storacha/capabilities@2.3.1': - dependencies: - '@ucanto/core': 10.4.6 - '@ucanto/interface': 11.0.1 - '@ucanto/principal': 9.0.3 - '@ucanto/transport': 9.2.1 - '@ucanto/validator': 10.0.1 - '@web3-storage/data-segment': 5.3.0 - multiformats: 13.4.2 - - '@storacha/client@2.1.3(encoding@0.1.13)': - dependencies: - '@ipld/dag-ucan': 3.4.5 - '@storacha/access': 1.6.9 - '@storacha/blob-index': 1.2.9 - '@storacha/capabilities': 2.3.1 - '@storacha/did-mailto': 1.0.2 - '@storacha/filecoin-client': 1.0.20 - '@storacha/upload-client': 1.4.2(encoding@0.1.13) - '@ucanto/client': 9.0.2 - '@ucanto/core': 10.4.6 - '@ucanto/interface': 11.0.1 - '@ucanto/principal': 9.0.3 - '@ucanto/transport': 9.2.1 - environment: 1.1.0 - transitivePeerDependencies: - - encoding - - '@storacha/did-mailto@1.0.2': {} - - '@storacha/filecoin-client@1.0.20': - dependencies: - '@ipld/dag-ucan': 3.4.5 - '@storacha/capabilities': 2.3.1 - '@ucanto/client': 9.0.2 - '@ucanto/core': 10.4.6 - '@ucanto/interface': 11.0.1 - '@ucanto/transport': 9.2.1 - - '@storacha/one-webcrypto@1.0.1': {} - - '@storacha/upload-client@1.4.2(encoding@0.1.13)': - dependencies: - '@ipld/car': 5.4.2 - '@ipld/dag-cbor': 9.2.5 - '@ipld/dag-ucan': 3.4.5 - '@ipld/unixfs': 3.0.0 - '@storacha/blob-index': 1.2.9 - '@storacha/capabilities': 2.3.1 - '@storacha/filecoin-client': 1.0.20 - '@ucanto/client': 9.0.2 - '@ucanto/core': 10.4.6 - '@ucanto/interface': 11.0.1 - '@ucanto/transport': 9.2.1 - '@web3-storage/data-segment': 5.3.0 - ipfs-utils: 9.0.14(encoding@0.1.13) - multiformats: 13.4.2 - p-retry: 5.1.2 - varint: 6.0.0 - transitivePeerDependencies: - - encoding - '@stylistic/eslint-plugin@5.10.0(eslint@9.39.4(jiti@2.6.1))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) @@ -14592,8 +14312,6 @@ snapshots: dependencies: '@types/unist': 3.0.3 - '@types/minimatch@3.0.5': {} - '@types/morgan@1.9.10': dependencies: '@types/node': 24.12.0 @@ -14622,7 +14340,7 @@ snapshots: dependencies: undici-types: 7.16.0 - '@types/node@25.5.0': + '@types/node@25.5.2': dependencies: undici-types: 7.18.2 @@ -14656,8 +14374,6 @@ snapshots: dependencies: csstype: 3.2.3 - '@types/retry@0.12.1': {} - '@types/send@1.2.1': dependencies: '@types/node': 24.12.0 @@ -14691,11 +14407,11 @@ snapshots: '@types/ws@7.4.7': dependencies: - '@types/node': 12.20.55 + '@types/node': 24.12.0 '@types/ws@8.18.1': dependencies: - '@types/node': 25.5.0 + '@types/node': 24.12.0 '@types/yargs-parser@21.0.3': {} @@ -14794,47 +14510,6 @@ snapshots: '@typescript-eslint/types': 8.57.2 eslint-visitor-keys: 5.0.1 - '@ucanto/client@9.0.2': - dependencies: - '@ucanto/core': 10.4.6 - '@ucanto/interface': 11.0.1 - - '@ucanto/core@10.4.6': - dependencies: - '@ipld/car': 5.4.2 - '@ipld/dag-cbor': 9.2.5 - '@ipld/dag-ucan': 3.4.5 - '@ucanto/interface': 11.0.1 - multiformats: 13.4.2 - - '@ucanto/interface@11.0.1': - dependencies: - '@ipld/dag-ucan': 3.4.5 - multiformats: 13.4.2 - - '@ucanto/principal@9.0.3': - dependencies: - '@ipld/dag-ucan': 3.4.5 - '@noble/curves': 1.9.7 - '@noble/ed25519': 1.7.5 - '@noble/hashes': 1.8.0 - '@ucanto/interface': 11.0.1 - multiformats: 13.4.2 - one-webcrypto: 1.0.3 - - '@ucanto/transport@9.2.1': - dependencies: - '@ucanto/core': 10.4.6 - '@ucanto/interface': 11.0.1 - - '@ucanto/validator@10.0.1': - dependencies: - '@ipld/car': 5.4.2 - '@ipld/dag-cbor': 9.2.5 - '@ucanto/core': 10.4.6 - '@ucanto/interface': 11.0.1 - multiformats: 13.4.2 - '@ungap/structured-clone@1.3.0': {} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -15549,29 +15224,13 @@ snapshots: multiformats: 13.4.2 uint8arrays: 5.1.0 - '@web3-storage/data-segment@5.3.0': - dependencies: - '@ipld/dag-cbor': 9.2.5 - multiformats: 13.4.2 - sync-multihash-sha2: 1.0.0 - - '@x402/core@2.8.0': + '@x402/core@2.9.0': dependencies: zod: 3.25.76 - '@x402/evm@2.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@x402/evm@2.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: - '@x402/core': 2.8.0 - viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - zod: 3.25.76 - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - '@x402/evm@2.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': - dependencies: - '@x402/core': 2.8.0 + '@x402/core': 2.9.0 viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: @@ -15579,9 +15238,9 @@ snapshots: - typescript - utf-8-validate - '@x402/evm@2.8.0(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)': + '@x402/evm@2.9.0(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)': dependencies: - '@x402/core': 2.8.0 + '@x402/core': 2.9.0 viem: 2.45.3(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: @@ -15589,12 +15248,12 @@ snapshots: - typescript - utf-8-validate - '@x402/express@2.8.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(express@5.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@x402/express@2.9.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(express@5.2.1)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: - '@x402/core': 2.8.0 - '@x402/extensions': 2.8.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10) + '@x402/core': 2.9.0 + '@x402/extensions': 2.9.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3)(utf-8-validate@6.0.6) express: 5.2.1 - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -15602,10 +15261,10 @@ snapshots: - typescript - utf-8-validate - '@x402/express@2.8.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(express@5.2.1)(typescript@6.0.2)(utf-8-validate@6.0.6)': + '@x402/express@2.9.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(express@5.2.1)(typescript@6.0.2)(utf-8-validate@6.0.6)': dependencies: - '@x402/core': 2.8.0 - '@x402/extensions': 2.8.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@6.0.2)(utf-8-validate@6.0.6) + '@x402/core': 2.9.0 + '@x402/extensions': 2.9.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@6.0.2)(utf-8-validate@6.0.6) express: 5.2.1 viem: 2.47.6(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 @@ -15615,16 +15274,16 @@ snapshots: - typescript - utf-8-validate - '@x402/extensions@2.8.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@x402/extensions@2.9.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: '@noble/curves': 1.9.7 '@scure/base': 1.2.6 - '@x402/core': 2.8.0 + '@x402/core': 2.9.0 ajv: 8.18.0 jose: 5.10.0 - siwe: 2.3.2(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + siwe: 2.3.2(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) tweetnacl: 1.0.3 - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -15632,11 +15291,11 @@ snapshots: - typescript - utf-8-validate - '@x402/extensions@2.8.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@6.0.2)(utf-8-validate@6.0.6)': + '@x402/extensions@2.9.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@6.0.2)(utf-8-validate@6.0.6)': dependencies: '@noble/curves': 1.9.7 '@scure/base': 1.2.6 - '@x402/core': 2.8.0 + '@x402/core': 2.9.0 ajv: 8.18.0 jose: 5.10.0 siwe: 2.3.2(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) @@ -15649,9 +15308,9 @@ snapshots: - typescript - utf-8-validate - '@x402/fetch@2.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': + '@x402/fetch@2.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: - '@x402/core': 2.8.0 + '@x402/core': 2.9.0 viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: @@ -15659,9 +15318,9 @@ snapshots: - typescript - utf-8-validate - '@x402/fetch@2.8.0(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)': + '@x402/fetch@2.9.0(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)': dependencies: - '@x402/core': 2.8.0 + '@x402/core': 2.9.0 viem: 2.45.3(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: @@ -15765,10 +15424,6 @@ snapshots: dependencies: humanize-ms: 1.2.1 - ajv-formats@2.1.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 - ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 @@ -15811,8 +15466,6 @@ snapshots: any-promise@1.3.0: {} - any-signal@3.0.1: {} - anymatch@3.1.3: dependencies: normalize-path: 3.0.0 @@ -15864,11 +15517,6 @@ snapshots: atomic-sleep@1.0.0: {} - atomically@2.1.1: - dependencies: - stubborn-fs: 2.0.0 - when-exit: 2.1.5 - attr-accept@2.2.5: {} available-typed-arrays@1.0.7: @@ -16035,8 +15683,6 @@ snapshots: big.js@6.2.2: {} - bigint-mod-arith@3.3.1: {} - bignumber.js@9.3.1: {} binary-extensions@2.3.0: {} @@ -16102,8 +15748,6 @@ snapshots: brorand@1.1.0: {} - browser-readablestream-to-it@1.0.3: {} - browser-resolve@2.0.0: dependencies: resolve: 1.22.11 @@ -16254,12 +15898,6 @@ snapshots: caniuse-lite@1.0.30001781: {} - carstream@2.3.0: - dependencies: - '@ipld/dag-cbor': 9.2.5 - multiformats: 13.4.2 - uint8arraylist: 2.4.8 - cashaddrjs@0.4.4: dependencies: big-integer: 1.6.36 @@ -16327,7 +15965,7 @@ snapshots: chrome-launcher@0.15.2: dependencies: - '@types/node': 22.19.15 + '@types/node': 24.12.0 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -16336,7 +15974,7 @@ snapshots: chromium-edge-launcher@0.2.0: dependencies: - '@types/node': 22.19.15 + '@types/node': 24.12.0 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -16437,17 +16075,6 @@ snapshots: readable-stream: 3.6.2 typedarray: 0.0.6 - conf@11.0.2: - dependencies: - ajv: 8.18.0 - ajv-formats: 2.1.1(ajv@8.18.0) - atomically: 2.1.1 - debounce-fn: 5.1.2 - dot-prop: 7.2.0 - env-paths: 3.0.0 - json-schema-typed: 8.0.2 - semver: 7.7.4 - confbox@0.1.8: {} connect@3.7.0: @@ -16608,10 +16235,6 @@ snapshots: dayjs@1.11.20: {} - debounce-fn@5.1.2: - dependencies: - mimic-fn: 4.0.0 - debug@2.6.9: dependencies: ms: 2.0.0 @@ -16709,11 +16332,7 @@ snapshots: domain-browser@4.22.0: {} - dot-prop@7.2.0: - dependencies: - type-fest: 2.19.0 - - dotenv@17.3.1: {} + dotenv@17.4.0: {} draggabilly@3.0.0: dependencies: @@ -16756,10 +16375,6 @@ snapshots: ee-first@1.1.1: {} - electron-fetch@1.9.1: - dependencies: - encoding: 0.1.13 - electron-to-chromium@1.5.328: {} elliptic@6.6.1: @@ -16801,6 +16416,7 @@ snapshots: encoding@0.1.13: dependencies: iconv-lite: 0.6.3 + optional: true end-of-stream@1.4.5: dependencies: @@ -16832,12 +16448,8 @@ snapshots: entities@6.0.1: {} - env-paths@3.0.0: {} - environment@1.1.0: {} - err-code@3.0.1: {} - error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -17254,8 +16866,6 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-fifo@1.3.2: {} - fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -17470,8 +17080,6 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-iterator@1.0.2: {} - get-nonce@1.0.1: {} get-package-type@0.1.0: {} @@ -17696,6 +17304,7 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 + optional: true iconv-lite@0.7.2: dependencies: @@ -17807,27 +17416,6 @@ snapshots: protons-runtime: 5.6.0 uint8arraylist: 2.4.8 - ipfs-utils@9.0.14(encoding@0.1.13): - dependencies: - any-signal: 3.0.1 - browser-readablestream-to-it: 1.0.3 - buffer: 6.0.3 - electron-fetch: 1.9.1 - err-code: 3.0.1 - is-electron: 2.2.2 - iso-url: 1.2.1 - it-all: 1.0.6 - it-glob: 1.0.2 - it-to-stream: 1.0.0 - merge-options: 3.0.4 - nanoid: 3.3.11 - native-fetch: 3.0.0(node-fetch@2.7.0(encoding@0.1.13)) - node-fetch: 2.7.0(encoding@0.1.13) - react-native-fetch-api: 3.0.0 - stream-to-it: 0.2.4 - transitivePeerDependencies: - - encoding - iron-webcrypto@1.2.1: {} is-alphabetical@2.0.1: {} @@ -17862,8 +17450,6 @@ snapshots: is-docker@2.2.1: {} - is-electron@2.2.2: {} - is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -17895,7 +17481,8 @@ snapshots: is-number@7.0.0: {} - is-plain-obj@2.1.0: {} + is-plain-obj@2.1.0: + optional: true is-plain-obj@4.1.0: {} @@ -17940,8 +17527,6 @@ snapshots: isexe@2.0.0: {} - iso-url@1.2.1: {} - isomorphic-timers-promises@1.0.1: {} isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10)): @@ -18005,17 +17590,10 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - it-all@1.0.6: {} - it-filter@3.1.4: dependencies: it-peekable: 3.0.8 - it-glob@1.0.2: - dependencies: - '@types/minimatch': 3.0.5 - minimatch: 3.1.5 - it-last@3.0.9: {} it-map@3.1.4: @@ -18050,15 +17628,6 @@ snapshots: it-stream-types@2.0.2: {} - it-to-stream@1.0.0: - dependencies: - buffer: 6.0.3 - fast-fifo: 1.3.2 - get-iterator: 1.0.2 - p-defer: 3.0.0 - p-fifo: 1.0.0 - readable-stream: 3.6.2 - jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -18208,7 +17777,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.19.15 + '@types/node': 24.12.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -18294,7 +17863,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.19.15 + '@types/node': 24.12.0 jest-util: 29.7.0 jest-mock@30.3.0: @@ -18553,8 +18122,6 @@ snapshots: json-schema-traverse@1.0.0: {} - json-schema-typed@8.0.2: {} - json-stable-stringify-without-jsonify@1.0.1: {} json-stable-stringify@1.3.0: @@ -18638,10 +18205,10 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - knip@5.88.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)(typescript@5.9.3): + knip@5.88.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(typescript@5.9.3): dependencies: '@nodelib/fs.walk': 1.2.8 - '@types/node': 25.5.0 + '@types/node': 25.5.2 fast-glob: 3.3.3 formatly: 0.3.0 jiti: 2.6.1 @@ -18904,6 +18471,7 @@ snapshots: merge-options@3.0.4: dependencies: is-plain-obj: 2.1.0 + optional: true merge-stream@2.0.0: {} @@ -19242,8 +18810,6 @@ snapshots: mimic-fn@2.1.0: {} - mimic-fn@4.0.0: {} - mimic-function@5.0.1: {} minimalistic-assert@1.0.1: {} @@ -19328,10 +18894,6 @@ snapshots: napi-postinstall@0.3.4: {} - native-fetch@3.0.0(node-fetch@2.7.0(encoding@0.1.13)): - dependencies: - node-fetch: 2.7.0(encoding@0.1.13) - natural-compare@1.4.0: {} negotiator@1.0.0: {} @@ -19476,8 +19038,6 @@ snapshots: dependencies: wrappy: 1.0.2 - one-webcrypto@1.0.3: {} - onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -19504,7 +19064,7 @@ snapshots: outdent@0.5.0: {} - ox@0.12.1(typescript@5.9.3)(zod@3.22.4): + ox@0.12.1(typescript@5.9.3)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -19512,14 +19072,14 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - zod - ox@0.12.1(typescript@5.9.3)(zod@3.25.76): + ox@0.12.1(typescript@5.9.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -19527,14 +19087,14 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - zod - ox@0.12.1(typescript@5.9.3)(zod@4.3.6): + ox@0.12.1(typescript@6.0.2)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -19542,14 +19102,14 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) + abitype: 1.2.3(typescript@6.0.2)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.2 transitivePeerDependencies: - zod - ox@0.12.1(typescript@6.0.2)(zod@3.25.76): + ox@0.14.7(typescript@5.9.3)(zod@3.22.4): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -19557,10 +19117,10 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@6.0.2)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) eventemitter3: 5.0.1 optionalDependencies: - typescript: 6.0.2 + typescript: 5.9.3 transitivePeerDependencies: - zod @@ -19612,8 +19172,8 @@ snapshots: ox@0.6.7(typescript@5.9.3)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) @@ -19649,15 +19209,8 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - p-defer@3.0.0: {} - p-defer@4.0.1: {} - p-fifo@1.0.0: - dependencies: - fast-fifo: 1.3.2 - p-defer: 3.0.0 - p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -19685,11 +19238,6 @@ snapshots: eventemitter3: 5.0.4 p-timeout: 6.1.4 - p-retry@5.1.2: - dependencies: - '@types/retry': 0.12.1 - retry: 0.13.1 - p-timeout@6.1.4: {} p-try@2.2.0: {} @@ -19814,6 +19362,11 @@ snapshots: pify@4.0.1: {} + pinata@2.5.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + pino-abstract-transport@0.5.0: dependencies: duplexify: 4.1.3 @@ -19934,7 +19487,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.19.15 + '@types/node': 24.12.0 long: 5.2.5 protobufjs@7.5.4: @@ -20134,10 +19687,6 @@ snapshots: react-lifecycles-compat: 3.0.4 warning: 4.0.3 - react-native-fetch-api@3.0.0: - dependencies: - p-defer: 3.0.0 - react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(utf-8-validate@5.0.10): dependencies: '@jest/create-cache-key-function': 29.7.0 @@ -20334,8 +19883,6 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - retry@0.13.1: {} - reusify@1.1.0: {} rfdc@1.4.1: {} @@ -20626,14 +20173,6 @@ snapshots: dependencies: semver: 7.7.4 - siwe@2.3.2(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)): - dependencies: - '@spruceid/siwe-parser': 2.1.2 - '@stablelib/random': 1.0.2 - ethers: 6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - uri-js: 4.4.1 - valid-url: 1.0.9 - siwe@2.3.2(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)): dependencies: '@spruceid/siwe-parser': 2.1.2 @@ -20784,10 +20323,6 @@ snapshots: stream-shift@1.0.3: {} - stream-to-it@0.2.4: - dependencies: - get-iterator: 1.0.2 - streamsearch@1.1.0: {} strict-uri-encode@2.0.0: {} @@ -20857,12 +20392,6 @@ snapshots: dependencies: js-tokens: 9.0.1 - stubborn-fs@2.0.0: - dependencies: - stubborn-utils: 1.0.2 - - stubborn-utils@1.0.2: {} - style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -20914,10 +20443,6 @@ snapshots: symbol-tree@3.2.4: {} - sync-multihash-sha2@1.0.0: - dependencies: - '@noble/hashes': 1.8.0 - synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 @@ -21121,8 +20646,6 @@ snapshots: type-fest@0.7.1: {} - type-fest@2.19.0: {} - type-fest@4.41.0: {} type-is@1.6.18: @@ -21433,23 +20956,6 @@ snapshots: - utf-8-validate - zod - viem@2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4): - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) - isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - ox: 0.12.1(typescript@5.9.3)(zod@3.22.4) - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - viem@2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 @@ -21518,6 +21024,23 @@ snapshots: - utf-8-validate - zod + viem@2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.14.7(typescript@5.9.3)(zod@3.22.4) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + viem@2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 @@ -21535,6 +21058,23 @@ snapshots: - utf-8-validate - zod + viem@2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + ox: 0.14.7(typescript@5.9.3)(zod@3.25.76) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + viem@2.47.6(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 @@ -21742,8 +21282,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - when-exit@2.1.5: {} - which-module@2.0.1: {} which-typed-array@1.1.20: diff --git a/server/package.json b/server/package.json index 3f94909..2103282 100644 --- a/server/package.json +++ b/server/package.json @@ -43,9 +43,7 @@ "@sentry/node": "^10.46.0", "@sentry/profiling-node": "^10.46.0", "@solana/web3.js": "^1.98.4", - "@storacha/client": "^2.1.3", "@types/bn.js": "^5.2.0", - "@ucanto/core": "^10.4.6", "@upstash/qstash": "^2.10.1", "@x402/core": "^2.8.0", "@x402/evm": "^2.8.0", @@ -63,6 +61,7 @@ "multer": "^2.1.1", "node-fetch": "^3.3.2", "pg": "^8.20.0", + "pinata": "2.5.5", "postgres": "^3.4.8", "resend": "^6.9.4", "ts-jest": "^29.4.6", diff --git a/server/src/controllers/agent.controller.ts b/server/src/controllers/agent.controller.ts index d99ebdb..0f4dc26 100644 --- a/server/src/controllers/agent.controller.ts +++ b/server/src/controllers/agent.controller.ts @@ -3,18 +3,16 @@ import { eq } from 'drizzle-orm' import { Request, Response } from 'express' import { db } from '../db/db.js' import { uploads } from '../db/schema.js' -import { computeCID } from '../utils/compute-cid.js' +import { pinCAR } from '../services/storage/pinata.service.js' +import { buildCAR } from '../utils/compute-cid.js' import { getAmountInUSD } from '../utils/constant.js' import { getExpiryDate } from '../utils/functions.js' import { logger } from '../utils/logger.js' -import { getPricingConfig, initStorachaClient } from '../utils/storacha.js' - -const SHARD_SIZE = 10_485_760 +import { getPricingConfig } from '../utils/pricing.js' // we should likely consider how renewal would work for agents, in the future. // for now, we can bank on the idea that agents may store for long-running course -// or epochs, say 2 years or mor. regardless, we still need to factor it. -// but for now, i'll rest. +// or epochs, say 2 years or more. regardless, we still need to factor it. /** * Agent file upload — runs after x402 payment middleware has verified the USDC payment. * @@ -47,7 +45,8 @@ export const uploadAgentFile = async (req: Request, res: Response) => { [file.originalname]: new Uint8Array(file.buffer), } - const computedCID = await computeCID(fileMap) + const { cid: computedCID, carBuffer } = await buildCAR(fileMap) + const existing = await db .select() .from(uploads) @@ -61,19 +60,13 @@ export const uploadAgentFile = async (req: Request, res: Response) => { expiresAt: existing[0].expiresAt, }) - const client = await initStorachaClient() - const fileObject = new File([file.buffer], file.originalname, { - type: file.mimetype, - }) - const uploadedCID = await client.uploadFile(fileObject, { - shardSize: SHARD_SIZE, - }) + const pinnedCID = await pinCAR(carBuffer, file.originalname) - if (uploadedCID.toString() !== computedCID) { - throw new Error( - `CID mismatch: computed=${computedCID}, uploaded=${uploadedCID}`, - ) - } + if (pinnedCID !== computedCID) + logger.warn('CID version mismatch between computed and pinned', { + computed: computedCID, + pinned: pinnedCID, + }) let payerAddress = 'agent' const xPayment = req.headers['x-payment'] as string | undefined @@ -82,7 +75,7 @@ export const uploadAgentFile = async (req: Request, res: Response) => { const decoded = JSON.parse(Buffer.from(xPayment, 'base64').toString()) payerAddress = decoded?.payload?.authorization?.from || 'agent' } catch { - // should leave it as 'agent'?? boya! + // leave it as 'agent' } } @@ -92,16 +85,13 @@ export const uploadAgentFile = async (req: Request, res: Response) => { // on-chain settlement happens asynchronously via Coinbase facilitator const transactionHash = `x402:base:${Date.now()}:${computedCID.slice(0, 12)}` - // USDC has 6 decimals compared to other ERC-20 tokens that uses 18 decimals. - // we should store amount in micro-USDC (same unit as USDC atomic units). - // apply the same $0.000001 floor the x402 middleware uses so we don't get - // a disparity between what the agent is charged and what we store in our db. + // USDC has 6 decimals. store amount in micro-USDC (same unit as USDC atomic units). + // apply the same $0.000001 floor the x402 middleware uses. const { ratePerBytePerDay } = await getPricingConfig() const costUSD = Math.max( getAmountInUSD(size, ratePerBytePerDay, duration), 0.000001, ) - // micro-USDC (6 decimals), e.g. 90000 = $0.09 const depositAmount = Math.ceil(costUSD * 1_000_000) const depositItem: typeof uploads.$inferInsert = { diff --git a/server/src/controllers/console.controller.ts b/server/src/controllers/console.controller.ts index cb53da0..680a052 100644 --- a/server/src/controllers/console.controller.ts +++ b/server/src/controllers/console.controller.ts @@ -2,7 +2,6 @@ import { Request, Response } from 'express' import { UsageService } from '../services/usage/usage.service.js' import { logger } from '../utils/logger.js' import { getEscrowBalance, withdrawFees } from '../utils/solana/index.js' -import { initStorachaClient } from '../utils/storacha.js' /** * get usage history @@ -10,12 +9,8 @@ import { initStorachaClient } from '../utils/storacha.js' export const getUsageHistory = async (req: Request, res: Response) => { try { const days = parseInt(req.query.days as string, 10) || 30 - - const storachaClient = await initStorachaClient() - const usageService = new UsageService(storachaClient) - await usageService.initialize() + const usageService = new UsageService() const history = await usageService.getUsageHistory(days) - return res.status(200).json({ success: true, data: history, @@ -34,24 +29,23 @@ export const getUsageHistory = async (req: Request, res: Response) => { */ export const getCurrentUsage = async (_req: Request, res: Response) => { try { - const storachaClient = await initStorachaClient() - const usageService = new UsageService(storachaClient) - await usageService.initialize() + const usageService = new UsageService() - const [storachaUsage, internalUsage] = await Promise.all([ - usageService.getStorachaUsage(), + const [pinataUsage, internalUsage] = await Promise.all([ + usageService.getUsage(), usageService.calculateInternalUsage(), ]) - const storachaBytes = storachaUsage.finalSize const planLimit = usageService.planLimit - const utilization = planLimit > 0 ? (storachaBytes / planLimit) * 100 : 0 + const utilization = + planLimit > 0 ? (pinataUsage.totalSizeBytes / planLimit) * 100 : 0 return res.status(200).json({ success: true, data: { - storacha: { - totalBytes: storachaBytes, + pinata: { + totalBytes: pinataUsage.totalSizeBytes, + pinCount: pinataUsage.pinCount, planLimit, utilizationPercentage: utilization, }, @@ -60,10 +54,10 @@ export const getCurrentUsage = async (_req: Request, res: Response) => { activeUploads: internalUsage.activeUploads, }, discrepancy: { - bytes: storachaBytes - internalUsage.totalBytes, + bytes: pinataUsage.totalSizeBytes - internalUsage.totalBytes, percentage: internalUsage.totalBytes > 0 - ? ((storachaBytes - internalUsage.totalBytes) / + ? ((pinataUsage.totalSizeBytes - internalUsage.totalBytes) / internalUsage.totalBytes) * 100 : 0, @@ -84,11 +78,8 @@ export const getCurrentUsage = async (_req: Request, res: Response) => { */ export const getUnresolvedAlerts = async (_req: Request, res: Response) => { try { - const storachaClient = await initStorachaClient() - const usageService = new UsageService(storachaClient) - await usageService.initialize() + const usageService = new UsageService() const alerts = await usageService.getUnresolvedAlerts() - return res.status(200).json({ success: true, data: alerts, @@ -111,12 +102,8 @@ export const resolveAlert = async (req: Request, res: Response) => { Array.isArray(req.params.id) ? req.params.id[0] : req.params.id, 10, ) - - const storachaClient = await initStorachaClient() - const usageService = new UsageService(storachaClient) - await usageService.initialize() + const usageService = new UsageService() await usageService.resolveAlert(alertId) - return res.status(200).json({ success: true, message: 'alert resolved', @@ -136,8 +123,6 @@ export const resolveAlert = async (req: Request, res: Response) => { export const getEscrowVaultBalance = async (_req: Request, res: Response) => { try { const balance = await getEscrowBalance() - - // convert bigint to string for JSON serialization return res.status(200).json({ success: true, data: { @@ -145,7 +130,6 @@ export const getEscrowVaultBalance = async (_req: Request, res: Response) => { totalClaimed: balance.totalClaimed.toString(), availableBalance: balance.availableBalance.toString(), accountLamports: balance.accountLamports.toString(), - availableBalanceSOL: Number(balance.availableBalance) / 1e9, totalDepositsSOL: Number(balance.totalDeposits) / 1e9, totalClaimedSOL: Number(balance.totalClaimed) / 1e9, diff --git a/server/src/controllers/jobs.controller.ts b/server/src/controllers/jobs.controller.ts index 83e3d54..5a012da 100644 --- a/server/src/controllers/jobs.controller.ts +++ b/server/src/controllers/jobs.controller.ts @@ -1,5 +1,3 @@ -import { UnknownLink } from '@storacha/client/types' -import { Link } from '@ucanto/core/schema' import { Request, Response } from 'express' import { batchUpdateWarningSentAt, @@ -8,9 +6,9 @@ import { updateDeletionStatus, } from '../db/uploads-table.js' import { sendBatchExpirationWarningEmail } from '../services/email/resend.service.js' +import { unpinCID } from '../services/storage/pinata.service.js' import { UsageService } from '../services/usage/usage.service.js' import { logger } from '../utils/logger.js' -import { initStorachaClient } from '../utils/storacha.js' /** * Checks for uploads needing expiration warnings and sends batched emails @@ -103,7 +101,7 @@ export const sendExpirationWarnings = async (_req: Request, res: Response) => { } /** - * Deletes expired deposits from Storacha + * Deletes expired uploads from Pinata and marks them as deleted in the DB */ export const deleteExpiredUploads = async (_req: Request, res: Response) => { try { @@ -121,7 +119,6 @@ export const deleteExpiredUploads = async (_req: Request, res: Response) => { logger.info('Found expired uploads to delete', { count: expiredDeposits.length, }) - const client = await initStorachaClient() for (const deposit of expiredDeposits) { try { @@ -133,10 +130,7 @@ export const deleteExpiredUploads = async (_req: Request, res: Response) => { status: deposit.deletionStatus, }) - const cid = Link.parse(deposit.contentCid) - await client.remove(cid as UnknownLink, { - shards: true, - }) + await unpinCID(deposit.contentCid) await updateDeletionStatus(deposit.id, 'deleted') logger.info('Successfully deleted this upload', { depositId: deposit.id, @@ -174,13 +168,8 @@ export const deleteExpiredUploads = async (_req: Request, res: Response) => { export const dailyUsageSnapshot = async (_req: Request, res: Response) => { try { logger.info('running daily usage snapshot job') - - const storachaClient = await initStorachaClient() - const usageService = new UsageService(storachaClient) - await usageService.initialize() - + const usageService = new UsageService() await usageService.createDailySnapshot() - return res.status(200).json({ success: true, message: 'daily usage snapshot completed', @@ -201,13 +190,8 @@ export const dailyUsageSnapshot = async (_req: Request, res: Response) => { export const weeklyUsageComparison = async (_req: Request, res: Response) => { try { logger.info('running weekly usage comparison job') - - const storachaClient = await initStorachaClient() - const usageService = new UsageService(storachaClient) - await usageService.initialize() - + const usageService = new UsageService() await usageService.compareUsage() - return res.status(200).json({ success: true, message: 'weekly usage comparison completed', diff --git a/server/src/controllers/pricing.controller.ts b/server/src/controllers/pricing.controller.ts index c3e9ba6..04ca77f 100644 --- a/server/src/controllers/pricing.controller.ts +++ b/server/src/controllers/pricing.controller.ts @@ -2,7 +2,7 @@ import { Request, Response } from 'express' import { getSolPrice } from '../services/price/sol-price.service.js' import { PaymentChain, QuoteOutput } from '../types.js' import { logger } from '../utils/logger.js' -import { getQuoteForFileUpload } from '../utils/storacha.js' +import { getQuoteForFileUpload } from '../utils/pricing.js' /** * Function to get Quote For File Upload diff --git a/server/src/controllers/storage.controller.ts b/server/src/controllers/storage.controller.ts index 8cc22a2..e9406ff 100644 --- a/server/src/controllers/storage.controller.ts +++ b/server/src/controllers/storage.controller.ts @@ -17,7 +17,7 @@ import { ONE_BILLION_LAMPORTS, } from '../utils/constant.js' import { logger } from '../utils/logger.js' -import { getPricingConfig } from '../utils/storacha.js' +import { getPricingConfig } from '../utils/pricing.js' import { createStorageRenewalTransaction } from './solana.controller.js' /** diff --git a/server/src/controllers/upload.controller.ts b/server/src/controllers/upload.controller.ts index a1505da..6d16da7 100644 --- a/server/src/controllers/upload.controller.ts +++ b/server/src/controllers/upload.controller.ts @@ -11,14 +11,15 @@ import { } from '../services/fil/verify.service.js' import { getSolPrice } from '../services/price/sol-price.service.js' import { PaginationContext } from '../types.js' -import { computeCID } from '../utils/compute-cid.js' +import { buildCAR, computeCID } from '../utils/compute-cid.js' import { DAY_TIME_IN_SECONDS, getAmountInLamportsFromUSD, } from '../utils/constant.js' import { getExpiryDate, getPaginationParams } from '../utils/functions.js' import { logger } from '../utils/logger.js' -import { getPricingConfig, initStorachaClient } from '../utils/storacha.js' +import { getPricingConfig } from '../utils/pricing.js' +import { gatewayUrl, pinCAR } from '../services/storage/pinata.service.js' import { createDepositTransaction } from './solana.controller.js' const MIN_DURATION_SECONDS = DAY_TIME_IN_SECONDS // 1 day @@ -26,17 +27,7 @@ const MIN_DURATION_SECONDS = DAY_TIME_IN_SECONDS // 1 day const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ /** - * i encountered an issue yesterday where uploading a 73MB folder took around 20.2mins and still ended up - * failing because of a timeout. the entire 73MB CAR was sent directly via blob/add on - * a slower connection 70Mbps download 5/10Mbps upload speed. we should break large files into smaller - * chunks by specifying the shard size. - * in this case, a 10MB (10 * 1024 * 1024 = 10,485,760` bytes) shard size would mean having around ~8 shards - * many requests. but each one finishes faster compared to the 73MB (and potentially large sizes in the future) chunk at once - */ -const SHARD_SIZE = 10_485_760 - -/** - * Function to upload a file to storacha + * Function to pin a file to IPFS via Pinata */ export const uploadFile = async (req: Request, res: Response) => { try { @@ -46,30 +37,27 @@ export const uploadFile = async (req: Request, res: Response) => { } const cid = req.query.cid as string if (!cid) return res.status(400).json({ message: 'CID is required' }) - const files = [ - new File([file.buffer], file.originalname, { type: file.mimetype }), - ] - const client = await initStorachaClient() - const uploadedCID = await client.uploadFile(files[0]) + const fileMap = { [file.originalname]: new Uint8Array(file.buffer) } + const { cid: computedCID, carBuffer } = await buildCAR(fileMap) - if (uploadedCID.toString() !== cid) { + if (computedCID !== cid) { throw new Error( - `CID mismatch! Precomputed: ${cid}, Uploaded: ${uploadedCID}`, + `CID mismatch! Precomputed: ${cid}, Built: ${computedCID}`, ) } - const uploadObject = { - cid: uploadedCID, - filename: file.originalname, - size: file.size, - type: file.mimetype, - url: `https://w3s.link/ipfs/${cid}/${file.originalname}`, - uploadedAt: new Date().toISOString(), + const pinnedCID = await pinCAR(carBuffer, file.originalname) + + if (pinnedCID !== computedCID) { + logger.warn('CID version mismatch between computed and pinned', { + computed: computedCID, + pinned: pinnedCID, + }) } Sentry.setContext('file-upload', { - cid, + cid: computedCID, fileName: file.originalname, fileSize: file.size, mimeType: file.mimetype, @@ -77,25 +65,32 @@ export const uploadFile = async (req: Request, res: Response) => { res.status(200).json({ message: 'Upload successful', - cid: uploadedCID, - object: uploadObject, + cid: computedCID, + object: { + cid: computedCID, + filename: file.originalname, + size: file.size, + type: file.mimetype, + url: gatewayUrl(computedCID, file.originalname), + uploadedAt: new Date().toISOString(), + }, }) } catch (error: any) { Sentry.captureException(error) - logger.error('Error uploading file to Storacha', { + logger.error('Error uploading file', { error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, cause: error?.cause, }) res.status(400).json({ - message: 'Error uploading file to directory', + message: 'Error uploading file', error: error instanceof Error ? error.message : String(error), }) } } /** - * Allows upload of multiple files or a directory to storacha + * Pins multiple files or a directory to IPFS via Pinata */ export const uploadFiles = async (req: Request, res: Response) => { try { @@ -106,46 +101,50 @@ export const uploadFiles = async (req: Request, res: Response) => { const cid = req.query.cid as string if (!cid) return res.status(400).json({ message: 'CID is required' }) - const fileObjects = files.map( - (f) => new File([f.buffer], f.originalname, { type: f.mimetype }), - ) + const fileMap: Record = {} + for (const f of files) { + fileMap[f.originalname] = new Uint8Array(f.buffer) + } - const client = await initStorachaClient() - const uploadedCID = await client.uploadDirectory(fileObjects, { - shardSize: SHARD_SIZE, - }) + const { cid: computedCID, carBuffer } = await buildCAR(fileMap) - if (uploadedCID.toString() !== cid) - throw new Error( - `CID mismatch! Computed: ${cid}, Uploaded: ${uploadedCID}`, - ) + if (computedCID !== cid) + throw new Error(`CID mismatch! Computed: ${cid}, Built: ${computedCID}`) + + const pinnedCID = await pinCAR( + carBuffer, + `directory-${crypto.randomUUID()}`, + ) + + if (pinnedCID !== computedCID) + logger.warn('CID version mismatch between computed and pinned', { + computed: computedCID, + pinned: pinnedCID, + }) Sentry.setContext('multi-file-upload', { - cid, + cid: computedCID, fileSize: files?.reduce((acc, curr) => acc + curr.size, 0), fileNames: files.map((f) => f.originalname), mimeTypes: files.map((f) => f.mimetype), }) Sentry.setTag('operation', 'multi-file-upload') - const uploadObject = { - cid: uploadedCID, - directoryName: `Upload-${crypto.randomUUID()}`, - url: `https://w3s.link/ipfs/${cid}`, - size: files.reduce((sum, f) => sum + f.size, 0), - files: files.map((f) => ({ - filename: f.originalname, - size: f.size, - type: f.mimetype, - url: `https://w3s.link/ipfs/${cid}/${f.originalname}`, - })), - uploadedAt: new Date().toISOString(), - } - res.status(200).json({ message: 'Upload successful', - cid: uploadedCID, - object: uploadObject, + cid: computedCID, + object: { + cid: computedCID, + url: gatewayUrl(computedCID), + size: files.reduce((sum, f) => sum + f.size, 0), + files: files.map((f) => ({ + filename: f.originalname, + size: f.size, + type: f.mimetype, + url: gatewayUrl(computedCID, f.originalname), + })), + uploadedAt: new Date().toISOString(), + }, }) } catch (error: any) { Sentry.captureException(error) @@ -631,7 +630,7 @@ export const confirmUpload = async (req: Request, res: Response) => { * * @remarks * Transaction verification will be implemented with indexer (see #176). - * SDK handles file upload to Storacha separately via /upload/file(s) endpoints. + * SDK handles file pinning to IPFS via Pinata via /upload/file(s) endpoints. */ export const verifyUsdFcPayment = async (req: Request, res: Response) => { try { diff --git a/server/src/controllers/user.controller.ts b/server/src/controllers/user.controller.ts deleted file mode 100644 index 95d826b..0000000 --- a/server/src/controllers/user.controller.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Capabilities } from '@storacha/client/types' -import { DID } from '@ucanto/core' -import * as Delegation from '@ucanto/core/delegation' -import { Link } from '@ucanto/core/schema' -import { Request, Response } from 'express' -import { logger } from '../utils/logger.js' -import { initStorachaClient } from '../utils/storacha.js' - -/** - * Function to create UCAN delegation to grant access of a space to an agent - * @param req - * @param res - * @returns - */ -export const createUCANDelegation = async (req: Request, res: Response) => { - try { - const { recipientDID, deadline, notBefore, baseCapabilities, fileCID } = - req.body - const client = await initStorachaClient() - const spaceDID = client.agent.did() - const audience = DID.parse(recipientDID) - const agent = client.agent - const capabilities: Capabilities = baseCapabilities.map((can: string) => ({ - with: `${spaceDID}`, - can, - nb: { - root: Link.parse(fileCID), - }, - })) - - const ucan = await Delegation.delegate({ - issuer: agent.issuer, - audience, - expiration: Number(deadline), - notBefore: Number(notBefore), - capabilities, - }) - - const archive = await ucan.archive() - if (!archive.ok) { - throw new Error('Failed to create delegation archive') - } - - return res.status(200).json({ - message: 'Delegation created successfully', - delegation: Buffer.from(archive.ok).toString('base64'), - }) - } catch (err) { - logger.error('Error creating UCAN delegation', { - error: err instanceof Error ? err.message : String(err), - }) - return res.status(500).json({ error: 'Failed to create delegation' }) - } -} diff --git a/server/src/index.ts b/server/src/index.ts index 55e7354..16ef537 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -15,7 +15,6 @@ import { solanaRouter } from './routes/solana.route.js' import { storageRouter } from './routes/storage.route.js' import { transactionsRouter } from './routes/transactions.route.js' import { uploadsRouter } from './routes/upload.route.js' -import { userRouter } from './routes/user.route.js' import { logger } from './utils/logger.js' import { ensureConfigInitialized } from './utils/solana/index.js' @@ -32,6 +31,8 @@ function validateEnv() { 'QSTASH_CURRENT_SIGNING_KEY', 'QSTASH_NEXT_SIGNING_KEY', 'RESEND_API_KEY', + 'PINATA_JWT', + 'PINATA_GATEWAY', ] const missing = requiredVars.filter((key) => !process.env[key]) @@ -88,7 +89,6 @@ app.use(apiLimiter) app.use('/console', consoleRouter) app.use('/upload', uploadsRouter) app.use('/storage', storageRouter) -app.use('/user', userRouter) app.use('/solana', solanaRouter) app.use('/jobs', jobsRouter) app.use('/health', serverRouter) diff --git a/server/src/middlewares/x402.middleware.ts b/server/src/middlewares/x402.middleware.ts index 81892da..aa5b76d 100644 --- a/server/src/middlewares/x402.middleware.ts +++ b/server/src/middlewares/x402.middleware.ts @@ -4,7 +4,7 @@ import { ExactEvmScheme } from '@x402/evm/exact/server' import { paymentMiddleware } from '@x402/express' import { getAmountInUSD } from '../utils/constant.js' import { logger } from '../utils/logger.js' -import { getPricingConfig } from '../utils/storacha.js' +import { getPricingConfig } from '../utils/pricing.js' const isMainnet = process.env.X402_NETWORK === 'mainnet' const BASE_NETWORK = isMainnet ? 'eip155:8453' : 'eip155:84532' diff --git a/server/src/routes/user.route.ts b/server/src/routes/user.route.ts deleted file mode 100644 index 045e527..0000000 --- a/server/src/routes/user.route.ts +++ /dev/null @@ -1,6 +0,0 @@ -import express from 'express' -import * as userController from '../controllers/user.controller.js' - -export const userRouter = express.Router() - -userRouter.post('/create-delegation', userController.createUCANDelegation) diff --git a/server/src/services/storage/pinata.service.ts b/server/src/services/storage/pinata.service.ts new file mode 100644 index 0000000..9013477 --- /dev/null +++ b/server/src/services/storage/pinata.service.ts @@ -0,0 +1,68 @@ +import { PinataSDK, pinnedFileCount, totalStorageUsage } from 'pinata' +import { logger } from '../../utils/logger.js' + +function getClient(): PinataSDK { + const jwt = process.env.PINATA_JWT + if (!jwt) throw new Error('PINATA_JWT env var not set') + return new PinataSDK({ pinataJwt: jwt }) +} + +/** + * Returns a gateway URL for a CID. + * Requires PINATA_GATEWAY env var to be set. + */ +export function gatewayUrl(cid: string, filename?: string): string { + const base = process.env.PINATA_GATEWAY + if (!base) throw new Error('PINATA_GATEWAY env var not set') + return filename ? `${base}/ipfs/${cid}/${filename}` : `${base}/ipfs/${cid}` +} + +/** + * Pins a CAR buffer to Pinata. + * The .car() builder flag tells Pinata to treat the upload as a CAR + * and use its root CID as the pin hash — so the returned CID matches + * our pre-computed CID from buildCAR. + */ +export async function pinCAR( + carBuffer: Uint8Array, + name: string, +): Promise { + const pinata = getClient() + const blob = new Blob([carBuffer], { type: 'application/vnd.ipld.car' }) + const file = new File([blob], `${name}.car`) + const result = await pinata.upload.public.file(file).name(name).car() + logger.info('pinata: CAR pinned', { cid: result.cid, name }) + return result.cid +} + +/** + * Removes a pin from Pinata by CID. + * Looks up the Pinata file ID first since the delete API takes IDs, not CIDs. + */ +export async function unpinCID(cid: string): Promise { + const pinata = getClient() + const list = await pinata.files.public.list().cid(cid) + if (!list.files.length) { + logger.warn('pinata: no file found for CID, skipping unpin', { cid }) + return + } + const fileId = list.files[0].id + await pinata.files.public.delete([fileId]) + logger.info('pinata: CID unpinned', { cid, fileId }) +} + +/** + * Returns total pinned file count and storage bytes from Pinata. + * Used by the usage monitoring service. + */ +export async function getPinataUsage(): Promise<{ + pinCount: number + totalSizeBytes: number +}> { + const pinata = getClient() + const [pinCount, totalSizeBytes] = await Promise.all([ + pinnedFileCount(pinata.config), + totalStorageUsage(pinata.config), + ]) + return { pinCount, totalSizeBytes } +} diff --git a/server/src/services/usage/usage.service.ts b/server/src/services/usage/usage.service.ts index 9e30fab..103607e 100644 --- a/server/src/services/usage/usage.service.ts +++ b/server/src/services/usage/usage.service.ts @@ -1,5 +1,3 @@ -import { Client as StorachaClient } from '@storacha/client' -import { AccountDID, PlanGetSuccess } from '@storacha/client/types' import { eq, sql } from 'drizzle-orm' import { Resend } from 'resend' import { db } from '../../db/db.js' @@ -9,6 +7,7 @@ import { usageAlerts, usageComparison, } from '../../db/schema.js' +import { getPinataUsage } from '../storage/pinata.service.js' import { logger } from '../../utils/logger.js' const { EMAIL_FROM, WATCHMAN } = process.env! @@ -22,39 +21,13 @@ const recipients: string | string[] = (() => { })() const resend = new Resend(process.env.RESEND_API_KEY) -const KiB = 1024 -const MiB = 1024 * KiB -const GiB = 1024 * MiB -const TiB = 1024 * GiB - -/** - * known plan capacities by product DID. - * - * storacha plans with overages enabled return limit: 0 from plan/get, - * meaning "unlimited with overage charges." the actual included capacity - * must be mapped from the product DID. for forge plans (strict limits), - * plan/get returns the real byte limit. - * - * see: https://github.com/storacha/specs/pull/150 - */ -const PLAN_CAPACITIES: Record = { - 'did:web:starter.storacha.network': 5 * GiB, - 'did:web:lite.storacha.network': 100 * GiB, - 'did:web:business.storacha.network': 2 * TiB, -} - -interface UsageReport { - finalSize: number - initialSize: number -} - export class UsageService { - private client: StorachaClient private planLimitBytes: number - constructor(client: StorachaClient, planLimitBytes: number = 5 * GiB) { - this.client = client - this.planLimitBytes = planLimitBytes // default 5GiB size + constructor() { + // Default to 1GB free tier. Set PINATA_PLAN_LIMIT_MB env var when upgrading. + const mb = parseInt(process.env.PINATA_PLAN_LIMIT_MB ?? '1024', 10) + this.planLimitBytes = mb * 1024 * 1024 } get planLimit(): number { @@ -62,115 +35,17 @@ export class UsageService { } /** - * get plan limit from storacha using plan/get capability. - * - * if limit is 0, it means overages are enabled and the plan is - * virtually unlimited. we use the product DID to look up the - * included capacity instead. - * - * @returns limit in bytes, or null if unlimited + * Fetch current storage usage from Pinata. */ - private async fetchPlanLimit(): Promise { - try { - const accountDID = process.env.STORACHA_ACCOUNT_DID as AccountDID - - if (!accountDID) { - logger.warn( - 'STORACHA_ACCOUNT_DID not set, using default plan limit. Set STORACHA_ACCOUNT_DID in environment variables.', - ) - return 5 * GiB - } - - const plan: PlanGetSuccess = - await this.client.capability.plan.get(accountDID) - - // used to have TS complain about an error here before. - // previous comment no longer needed. this too would go away later. browse the commit - // history for your perusal if you want. - const limitBytes = parseInt(plan.limit, 10) - - // limit: 0 means overages enabled — look up included capacity from product DID - if (limitBytes === 0) { - const product = plan.product - const knownLimit = product ? PLAN_CAPACITIES[product] : undefined - - if (knownLimit) { - logger.info('plan has overages enabled, using known capacity', { - product, - limitBytes: knownLimit, - limitGB: (knownLimit / 1e9).toFixed(2), - }) - return knownLimit - } - - logger.warn('unknown product DID, using default capacity', { - product, - defaultGB: '5.00', - }) - return 5 * GiB - } - - // forge plans return the actual byte limit - logger.info('plan limit fetched from storacha', { - accountDID, - limitBytes, - limitGB: (limitBytes / 1e9).toFixed(2), - }) - - return limitBytes - } catch (error) { - logger.error('failed to fetch plan limit from storacha', { error }) - return 5 * GiB - } - } - - /** - * init the service - */ - async initialize(): Promise { - const fetchedLimit = await this.fetchPlanLimit() - if (fetchedLimit !== null) { - this.planLimitBytes = fetchedLimit - logger.info('usage service initialized with plan limit', { - limitBytes: this.planLimitBytes, - limitGB: (this.planLimitBytes / 1e9).toFixed(2), - }) - } else { - logger.info('usage service initialized with unlimited plan') - } - } - - /** - * fetch current usage from storacha using capability.usage.report - */ - async getStorachaUsage( - from: Date = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), - to: Date = new Date(), - ): Promise { + async getUsage(): Promise<{ + totalSizeBytes: number + pinCount: number + }> { try { - const currentSpace = this.client.currentSpace() - if (!currentSpace) throw new Error('no storacha space available') - - const spaceDid = currentSpace.did() - const report = await this.client.capability.usage.report(spaceDid, { - from, - to, - }) - - // report is Record, get the first/only entry - const usageData = Object.values(report)[0] - - if (!usageData) { - throw new Error('no usage data returned from storacha') - } - - return { - finalSize: usageData.size.final, - initialSize: usageData.size.initial, - } + return await getPinataUsage() } catch (error) { - logger.error('failed to fetch storacha usage report', { error }) - throw new Error('failed to fetch storacha usage') + logger.error('failed to fetch pinata usage', { error }) + throw new Error('failed to fetch pinata usage') } } @@ -200,21 +75,19 @@ export class UsageService { */ async createDailySnapshot(): Promise { try { - const [storachaUsage, internalUsage] = await Promise.all([ - this.getStorachaUsage(), + const [pinataUsage, internalUsage] = await Promise.all([ + this.getUsage(), this.calculateInternalUsage(), ]) - const storachaBytes = storachaUsage.finalSize + const storedBytes = pinataUsage.totalSizeBytes const utilization = - this.planLimitBytes > 0 - ? (storachaBytes / this.planLimitBytes) * 100 - : 0 + this.planLimitBytes > 0 ? (storedBytes / this.planLimitBytes) * 100 : 0 await db.insert(usage).values({ totalBytesStored: internalUsage.totalBytes, totalActiveUploads: internalUsage.activeUploads, - storachaReportedBytes: storachaBytes, + storachaReportedBytes: storedBytes, storachaPlanLimit: this.planLimitBytes, utilizationPercentage: utilization, }) @@ -222,15 +95,11 @@ export class UsageService { logger.info('daily usage snapshot created', { totalBytes: internalUsage.totalBytes, activeUploads: internalUsage.activeUploads, - storachaReportedBytes: storachaBytes, + pinataReportedBytes: storedBytes, utilization: `${utilization.toFixed(2)}%`, }) - await this.checkThresholds( - utilization, - storachaBytes, - this.planLimitBytes, - ) + await this.checkThresholds(storedBytes, this.planLimitBytes, utilization) } catch (error) { logger.error('failed to create daily snapshot', { error }) throw error @@ -238,17 +107,17 @@ export class UsageService { } /** - * weekly comparison between our data and the capability report + * weekly comparison between our internal data and the pinata report */ async compareUsage(): Promise { try { - const [storachaUsage, internalUsage] = await Promise.all([ - this.getStorachaUsage(), + const [pinataUsage, internalUsage] = await Promise.all([ + this.getUsage(), this.calculateInternalUsage(), ]) - const storachaBytes = storachaUsage.finalSize - const discrepancy = storachaBytes - internalUsage.totalBytes + const pinataBytes = pinataUsage.totalSizeBytes + const discrepancy = pinataBytes - internalUsage.totalBytes const discrepancyPercentage = internalUsage.totalBytes > 0 ? (Math.abs(discrepancy) / internalUsage.totalBytes) * 100 @@ -263,19 +132,19 @@ export class UsageService { await db.insert(usageComparison).values({ ourCalculatedBytes: internalUsage.totalBytes, - storachaReportedBytes: storachaBytes, + storachaReportedBytes: pinataBytes, discrepancyBytes: discrepancy, discrepancyPercentage, status, notes: status !== 'ok' - ? `discrepancy of ${(discrepancy / 1e9).toFixed(2)} GB (${discrepancyPercentage.toFixed(2)}%)` // 1e9 = 1GB in bytes + ? `discrepancy of ${(discrepancy / 1e9).toFixed(2)} GB (${discrepancyPercentage.toFixed(2)}%)` : null, }) logger.info('usage comparison completed', { ourBytes: internalUsage.totalBytes, - storachaBytes, + pinataBytes, discrepancy, status, }) @@ -297,9 +166,9 @@ export class UsageService { * check utilization thresholds and create alerts */ private async checkThresholds( - utilization: number, bytesStored: number, planLimit: number, + utilization: number, ): Promise { const thresholds = [ { level: 80, type: 'threshold_80', alertLevel: 'warning' as const }, @@ -309,7 +178,6 @@ export class UsageService { for (const threshold of thresholds) { if (utilization >= threshold.level) { - // check if alert already exists and is unresolved const existingAlert = await db .select() .from(usageAlerts) @@ -363,7 +231,7 @@ export class UsageService { ${alert.alertLevel === 'critical' ? '🔴' : '⚠️'} storage ${alert.alertLevel} alert

${alert.message}

- + ${ alert.utilizationPercentage ? ` @@ -385,7 +253,7 @@ export class UsageService { ` : '' } - +

alert type: ${alert.alertType}
created: ${new Date().toISOString()} diff --git a/server/src/tests/User/createDelegation.test.ts b/server/src/tests/User/createDelegation.test.ts deleted file mode 100644 index bfebb91..0000000 --- a/server/src/tests/User/createDelegation.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -/// __tests__/delegation.test.ts - -import axios from 'axios' -import dotenv from 'dotenv' - -dotenv.config() - -const BASE_URL = `http://localhost:${process.env.PORT}/api/user` - -test('Create delegation on behalf of the user', async () => { - const DelegationObject = { - recipientDID: - 'did:key:z4MXj1wBzi9jUstyP6ZRFhxGBEZDEnxMvBGB1DREJC4Q1cPgcmtYgETAQHDXiJTKzGHT7rPnZuMn2ofNjDJ3mUUWAYt8mzFfmR6DBGt21eESHyeNqiY7nzGGkcX6kjaR8LaGX2i29Qf75hFpEHsbrKGHd87UwLY9Ut3UfeU4Z7uhj2eTnSC1qDWM3vzyGT1tRoxXkcbviw1pwu6mNKcN1VJngW52x2y6L7hGRbTRr81vLiwCnTVxJ4kHTUHbW2u3ijA64iVEEdYr4yqqbfnDLp6F1PYuB6kfMX16Gkd7Dyw3GBSVSe3ba2NaitqPpqMC4iHSpNMPZfwFg1hBEeCTWWgDGA3xjyzYeFDWmc39fov7qXUTTWMgt', - deadline: 1753108795, - notBefore: 1753107795, - baseCapabilities: ['file/upload'], - fileCID: 'bafkreiahejox56s3d26zpojexilpwofks6yllrdfzye2tmuiu6kgzkuixi', - proof: process.env.STORACHA_PROOF, - storachaKey: process.env.STORACHA_KEY, - } - const result = await axios.post( - `${BASE_URL}/create-delegation`, - DelegationObject, - ) - // expect(typeof result.data.message).toBe(string); - expect(result.data.message).toBe('Delegation created successfully') -}) diff --git a/server/src/utils/compute-cid.ts b/server/src/utils/compute-cid.ts index 6197404..cf1a152 100644 --- a/server/src/utils/compute-cid.ts +++ b/server/src/utils/compute-cid.ts @@ -6,89 +6,76 @@ import { import { logger } from './logger.js' /** - * pre-computes the Storacha/IPFS-compatible CID for a file/directory - * - * This is necessary for us to ensure that a deposit is actually made before - * delegations to store data is provided. + * Builds a CAR from a file map and returns the root CID + raw CAR bytes. + * Use this when you need both the CID and the bytes to upload to a pinning service. */ -export async function computeCID( +export async function buildCAR( fileMap: Record, -): Promise { - try { - if (Object.keys(fileMap).length === 1) { - const [_, content] = Object.entries(fileMap)[0] - const file = new Blob([content]) - - let rootCID: any +): Promise<{ cid: string; carBuffer: Uint8Array }> { + const chunks: Uint8Array[] = [] + let rootCID: any - await createFileEncoderStream(file) - .pipeThrough( - new TransformStream({ - transform(block, controller) { - rootCID = block.cid - controller.enqueue(block) - }, - }), + const encoderStream = + Object.keys(fileMap).length === 1 + ? createFileEncoderStream(new Blob([Object.values(fileMap)[0]])) + : createDirectoryEncoderStream( + Object.entries(fileMap).map(([name, content]) => ({ + name, + stream: () => + new ReadableStream({ + start(controller) { + controller.enqueue(content) + controller.close() + }, + }), + })), ) - .pipeThrough(new CAREncoderStream()) - .pipeTo(new WritableStream()) - - return rootCID.toString() - } - - return await computeDirectoryCID(fileMap) - } catch (error) { - logger.error('Error computing CID', { - error: error instanceof Error ? error.message : String(error), - }) - throw new Error( - `Failed to compute CID: ${error instanceof Error ? error.message : String(error)}`, - ) - } -} - -/** - * Compute CID for directory (multiple files) - * This matches how Storacha handles directory uploads - */ -async function computeDirectoryCID( - fileMap: Record, -): Promise { - // need to "pack" the files into an ipfs-compatible format - const files = Object.entries(fileMap).map(([name, content]) => ({ - name, - // would've just passed the destructured `content` as is below instead of this - // but TS complains that ipfs-car's FileLike needs to be inferred - stream: () => - new ReadableStream({ - start(controller) { - controller.enqueue(content) - controller.close() - }, - }), - })) - let rootCID: any - let blockCount = 0 - - await createDirectoryEncoderStream(files) + await encoderStream .pipeThrough( new TransformStream({ transform(block, controller) { - blockCount++ - // For directories, we want the final CID that represents the directory itself - // This is usually the last block, but we can also check if it's a directory type rootCID = block.cid controller.enqueue(block) }, }), ) .pipeThrough(new CAREncoderStream()) - .pipeTo(new WritableStream()) + .pipeTo( + new WritableStream({ + write(chunk) { + chunks.push(new Uint8Array(chunk)) + }, + }), + ) + + const total = chunks.reduce((sum, c) => sum + c.length, 0) + const carBuffer = new Uint8Array(total) + let offset = 0 + for (const chunk of chunks) { + carBuffer.set(chunk, offset) + offset += chunk.length + } + + return { cid: rootCID.toString(), carBuffer } +} - logger.info('Directory CID computed', { - blockCount, - rootCid: rootCID?.toString(), - }) - return rootCID.toString() +/** + * Pre-computes the CID for a file or directory without retaining the CAR bytes. + * Used in the deposit flow where only the CID is needed. + */ +export async function computeCID( + fileMap: Record, +): Promise { + try { + const { cid } = await buildCAR(fileMap) + return cid + } catch (error) { + logger.error('Error computing CID', { + error: error instanceof Error ? error.message : String(error), + }) + throw new Error( + `Failed to compute CID: ${error instanceof Error ? error.message : String(error)}`, + ) + } } diff --git a/server/src/utils/storacha.ts b/server/src/utils/pricing.ts similarity index 61% rename from server/src/utils/storacha.ts rename to server/src/utils/pricing.ts index d41f3ca..87888b4 100644 --- a/server/src/utils/storacha.ts +++ b/server/src/utils/pricing.ts @@ -1,8 +1,3 @@ -import * as Client from '@storacha/client' -import { create } from '@storacha/client' -import { Signer } from '@storacha/client/principal/ed25519' -import * as Proof from '@storacha/client/proof' -import { StoreMemory } from '@storacha/client/stores/memory' import { db } from '../db/db.js' import { configTable } from '../db/schema.js' import { getSolPrice } from '../services/price/sol-price.service.js' @@ -10,33 +5,6 @@ import { QuoteInput } from '../types.js' import { getAmountInLamportsFromUSD, getAmountInUSD } from './constant.js' import { logger } from './logger.js' -/** - * Initializes a Storacha client using user-provided key and proof. - * - * @returns {Promise} Initialized W3UP client - */ -export async function initStorachaClient(): Promise { - const principal = Signer.parse(process.env.STORACHA_KEY!) - const store = new StoreMemory() - const client = await create({ principal, store }) - - const proof = await Proof.parse(process.env.STORACHA_PROOF!) - const space = await client.addSpace(proof) - await client.setCurrentSpace(space.did()) - - // we had errors on the server because the proof for this delegation - // wasn't provided. the plan/get capability cannot be scoped to the server (space DID?) agent - // so the appropriate thing to do is to get the plan proof separately with sacap: https://github.com/kaf-lamed-beyt/sacap - // since it is an account-level capability (did:mailto...) so the - // plan limit call in the usage service doesn't error. - if (process.env.STORACHA_PLAN_PROOF) { - const planProof = await Proof.parse(process.env.STORACHA_PLAN_PROOF) - await client.addProof(planProof) - } - - return client -} - /** * This function returns the amount it will cost to upload the file, keep it for minimum duration */ From 38a0367cc61c4668bcdad26057c0a7fc3b6f9b0f Mon Sep 17 00:00:00 2001 From: Caleb! <48127194+kaf-lamed-beyt@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:48:02 +0100 Subject: [PATCH 2/3] feat: return gateway urls from server, remove w3s.link hardcoding (#10) * feat: return gateway urls from server, remove w3s.link hardcoding server now attaches a `url` field to upload history records and renewal responses using the pinata gateway. sol sdk consumes the url from the server instead of constructing it from a hardcoded w3s.link url. * chore: add changeset --- .changeset/sol-gateway-url-from-server.md | 5 +++++ packages/sol/src/payment.ts | 7 +++---- server/src/controllers/jobs.controller.ts | 2 +- server/src/controllers/storage.controller.ts | 15 ++++++++++++++- server/src/controllers/transactions.controller.ts | 2 +- server/src/controllers/upload.controller.ts | 2 +- server/src/db/{uploads-table.ts => uploads.ts} | 13 ++++++++++++- ui/src/hooks/upload-history.ts | 5 +---- 8 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 .changeset/sol-gateway-url-from-server.md rename server/src/db/{uploads-table.ts => uploads.ts} (96%) diff --git a/.changeset/sol-gateway-url-from-server.md b/.changeset/sol-gateway-url-from-server.md new file mode 100644 index 0000000..04b226c --- /dev/null +++ b/.changeset/sol-gateway-url-from-server.md @@ -0,0 +1,5 @@ +--- +"@toju.network/sol": patch +--- + +renewal response now returns the gateway url from the server instead of a hardcoded w3s.link url diff --git a/packages/sol/src/payment.ts b/packages/sol/src/payment.ts index 403e565..35379c1 100644 --- a/packages/sol/src/payment.ts +++ b/packages/sol/src/payment.ts @@ -336,15 +336,14 @@ export async function renewStorageTxn( }, ) - if (!confirmRenewalTx.ok) { - console.error('Failed to confirm renewal') - } + if (!confirmRenewalTx.ok) console.error('Failed to confirm renewal') + const confirmationData = await confirmRenewalTx.json().catch(() => ({})) return { success: true, cid, signature: signature as Signature, - url: `https://w3s.link/ipfs/${cid}`, + url: confirmationData?.url || '', message: 'Storage renewed successfully', } } diff --git a/server/src/controllers/jobs.controller.ts b/server/src/controllers/jobs.controller.ts index 5a012da..e741bbd 100644 --- a/server/src/controllers/jobs.controller.ts +++ b/server/src/controllers/jobs.controller.ts @@ -4,7 +4,7 @@ import { getExpiredDeposits, getUploadsGroupedByEmail, updateDeletionStatus, -} from '../db/uploads-table.js' +} from '../db/uploads.js' import { sendBatchExpirationWarningEmail } from '../services/email/resend.service.js' import { unpinCID } from '../services/storage/pinata.service.js' import { UsageService } from '../services/usage/usage.service.js' diff --git a/server/src/controllers/storage.controller.ts b/server/src/controllers/storage.controller.ts index e9406ff..065b6c2 100644 --- a/server/src/controllers/storage.controller.ts +++ b/server/src/controllers/storage.controller.ts @@ -3,11 +3,12 @@ import { eq } from 'drizzle-orm' import { Request, Response } from 'express' import { db } from '../db/db.js' import { configTable, uploads } from '../db/schema.js' -import { renewStorageDuration, saveTransaction } from '../db/uploads-table.js' +import { renewStorageDuration, saveTransaction } from '../db/uploads.js' import { getUsdfcContractAddress, verifyErc20Transfer, } from '../services/fil/verify.service.js' +import { gatewayUrl } from '../services/storage/pinata.service.js' import { getSolPrice } from '../services/price/sol-price.service.js' import { getAmountInLamportsFromUSD, @@ -226,6 +227,12 @@ export const confirmStorageRenewal = async (req: Request, res: Response) => { return res.status(200).json({ message: 'Storage renewed successfully', deposit: updated, + url: gatewayUrl( + updated.contentCid, + updated.fileType === 'directory' + ? undefined + : (updated.fileName ?? undefined), + ), }) } catch (error) { logger.error('Error confirming renewal', { @@ -391,6 +398,12 @@ export const confirmRenewalUsdFC = async (req: Request, res: Response) => { verified: true, message: 'Storage renewed successfully with USDFC', deposit: updated, + url: gatewayUrl( + updated.contentCid, + updated.fileType === 'directory' + ? undefined + : (updated.fileName ?? undefined), + ), }) } catch (error) { Sentry.captureException(error) diff --git a/server/src/controllers/transactions.controller.ts b/server/src/controllers/transactions.controller.ts index 324077d..df7713b 100644 --- a/server/src/controllers/transactions.controller.ts +++ b/server/src/controllers/transactions.controller.ts @@ -1,5 +1,5 @@ import { Request, Response } from 'express' -import { getTransactionsForCID } from '../db/uploads-table.js' +import { getTransactionsForCID } from '../db/uploads.js' import { logger } from '../utils/logger.js' /** diff --git a/server/src/controllers/upload.controller.ts b/server/src/controllers/upload.controller.ts index 6d16da7..db64d31 100644 --- a/server/src/controllers/upload.controller.ts +++ b/server/src/controllers/upload.controller.ts @@ -4,7 +4,7 @@ import { eq } from 'drizzle-orm' import { Request, Response } from 'express' import { db } from '../db/db.js' import { configTable, uploads } from '../db/schema.js' -import { getUserHistory, saveTransaction } from '../db/uploads-table.js' +import { getUserHistory, saveTransaction } from '../db/uploads.js' import { getUsdfcContractAddress, verifyErc20Transfer, diff --git a/server/src/db/uploads-table.ts b/server/src/db/uploads.ts similarity index 96% rename from server/src/db/uploads-table.ts rename to server/src/db/uploads.ts index 0c45222..a7c8557 100644 --- a/server/src/db/uploads-table.ts +++ b/server/src/db/uploads.ts @@ -1,4 +1,5 @@ import { and, desc, eq, inArray, isNull, lte, or, sql } from 'drizzle-orm' +import { gatewayUrl } from '../services/storage/pinata.service.js' import { PaginationContext } from '../types.js' import { logger } from '../utils/logger.js' import { db } from './db.js' @@ -59,8 +60,18 @@ export const getUserHistory = async ( const buildPageUrl = (p: number) => `${ctx?.baseUrl}${ctx?.path}?userAddress=${userAddress}&page=${p}&limit=${limit}&chain=${chain}` + const records = data.map((record) => ({ + ...record, + url: gatewayUrl( + record.contentCid, + record.fileType === 'directory' + ? undefined + : (record.fileName ?? undefined), + ), + })) + return { - data, + data: records, total, page, pageSize: limit, diff --git a/ui/src/hooks/upload-history.ts b/ui/src/hooks/upload-history.ts index f0355c2..cea2fb9 100644 --- a/ui/src/hooks/upload-history.ts +++ b/ui/src/hooks/upload-history.ts @@ -73,10 +73,7 @@ export function useUploadHistory() { filename: deposit.fileName || 'Unknown File', size: Number(deposit.fileSize) || 0, type: deposit.fileType || 'application/octet-stream', - url: - deposit.fileType === 'directory' - ? `https://w3s.link/ipfs/${deposit.contentCid}` - : `https://w3s.link/ipfs/${deposit.contentCid}${deposit.fileName ? `/${deposit.fileName}` : ''}`, + url: deposit.url || '', uploadedAt: deposit.createdAt, signature: deposit.transactionHash || '', From a2fd15afe7cc2758b55909c98cc80b4150ffe506 Mon Sep 17 00:00:00 2001 From: Caleb! <48127194+kaf-lamed-beyt@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:04:52 +0100 Subject: [PATCH 3/3] refactor!: overhaul of the existing upload flow to accommodate the pinata migration. (#11) * refactor!: overhaul of the existing upload flow to accommodate the pinata migration. initially, our upload flow had various steps in between what defines a successful upload. from computing the CID with storacha's `ipfs-car` and comparing if it matches what is generated on a successful upload+deposit, to cmaking another roundtrip to verify that pre-computed CID matches what's been paid for, and then confirming again. all this to ensure whatever is stored is actually paid for. with this new migration, and the sudden foreclosure of storacha, however sad that may be, the previous design is pretty much useless and obsolete now. so we pin directly to IPFS, and claenup debtors if peradventure the upload fails halfway and they do not continue/resume it to pay in 24 hours. * chore: remove unused deps --- .changeset/sol-gateway-url-from-server.md | 5 - packages/fil/CHANGELOG.md | 10 + packages/fil/package.json | 2 +- packages/fil/src/payment.ts | 51 +-- packages/fil/src/types.ts | 6 +- packages/sol/CHANGELOG.md | 14 + packages/sol/package.json | 2 +- packages/sol/src/payment.ts | 60 +-- pnpm-lock.yaml | 194 ++++---- server/package.json | 1 - server/src/controllers/agent.controller.ts | 18 +- server/src/controllers/jobs.controller.ts | 54 +++ server/src/controllers/upload.controller.ts | 430 +++++++++--------- server/src/db/uploads.ts | 35 +- server/src/routes/jobs.route.ts | 6 + server/src/services/storage/pinata.service.ts | 27 +- server/src/tests/Admin/updateDuration.test.ts | 38 -- server/src/utils/compute-cid.ts | 97 ++-- ui/package.json | 4 +- 19 files changed, 491 insertions(+), 563 deletions(-) delete mode 100644 .changeset/sol-gateway-url-from-server.md delete mode 100644 server/src/tests/Admin/updateDuration.test.ts diff --git a/.changeset/sol-gateway-url-from-server.md b/.changeset/sol-gateway-url-from-server.md deleted file mode 100644 index 04b226c..0000000 --- a/.changeset/sol-gateway-url-from-server.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@toju.network/sol": patch ---- - -renewal response now returns the gateway url from the server instead of a hardcoded w3s.link url diff --git a/packages/fil/CHANGELOG.md b/packages/fil/CHANGELOG.md index ab28f52..569d106 100644 --- a/packages/fil/CHANGELOG.md +++ b/packages/fil/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 1.0.0 + +### Major Changes + +- upload flow now pins to pinata at deposit time — the file upload step after confirm is no longer needed and has been removed + + breaking: deposit response no longer includes depositMetadata + breaking: confirm no longer accepts depositMetadata in the request body + breaking: url is now returned from the confirm/verify-payment response instead of the file upload response + ## 0.2.2 ### Patch Changes diff --git a/packages/fil/package.json b/packages/fil/package.json index 281a235..e9a4dae 100644 --- a/packages/fil/package.json +++ b/packages/fil/package.json @@ -1,6 +1,6 @@ { "name": "@toju.network/fil", - "version": "0.2.2", + "version": "1.0.0", "description": "Pay for storage with USDFC on IPFS using Storacha's Hot storage network.", "license": "Apache-2.0", "type": "module", diff --git a/packages/fil/src/payment.ts b/packages/fil/src/payment.ts index 53a7e13..e221909 100644 --- a/packages/fil/src/payment.ts +++ b/packages/fil/src/payment.ts @@ -63,7 +63,6 @@ export async function createDepositTxn( { transactionHash: txHash, cid: depositRes.cid, - depositMetadata: depositRes.depositMetadata, }, apiEndpoint, ) @@ -73,55 +72,12 @@ export async function createDepositTxn( verifyRes.message || 'Payment verification failed on server', ) - const uploadData = new FormData() - file.forEach((file) => uploadData.append('file', file)) - let fileUploadReq - const isMultipleFiles = file.length > 1 - - if (isMultipleFiles) { - fileUploadReq = await fetch( - `${apiEndpoint}/upload/files?cid=${encodeURIComponent(depositRes.cid)}`, - { - method: 'POST', - body: uploadData, - }, - ) - } else { - fileUploadReq = await fetch( - `${apiEndpoint}/upload/file?cid=${encodeURIComponent(depositRes.cid)}`, - { - method: 'POST', - body: uploadData, - }, - ) - } - - if (!fileUploadReq.ok) { - let err = 'Unknown error' - try { - const data: DepositResponse = await fileUploadReq.json() - err = data.message || err - } catch {} - throw new Error('Deposit API error: ' + err) - } - - const uploadResponse: Pick = - await fileUploadReq?.json() - return { success: true, transactionHash: txHash, cid: depositRes.cid, - url: uploadResponse.object.url, - message: uploadResponse.object.message, - fileInfo: uploadResponse.object - ? { - type: uploadResponse?.object?.fileInfo?.type || '', - size: uploadResponse?.object?.fileInfo?.size || 0, - uploadedAt: uploadResponse?.object?.fileInfo?.uploadedAt || '', - filename: uploadResponse?.object?.fileInfo?.filename || '', - } - : undefined, + url: verifyRes.url || '', + message: verifyRes.message || '', } } catch (error) { const errorMessage = @@ -155,7 +111,7 @@ export async function verifyPayment( args: VerifyPaymentArgs, apiEndpoint: string, ): Promise { - const { transactionHash, cid, depositMetadata } = args + const { transactionHash, cid } = args const verifyReq = await fetch(`${apiEndpoint}/upload/fil/verify-payment`, { method: 'POST', @@ -165,7 +121,6 @@ export async function verifyPayment( body: JSON.stringify({ transactionHash, cid, - depositMetadata, }), }) diff --git a/packages/fil/src/types.ts b/packages/fil/src/types.ts index de172ef..de8a29f 100644 --- a/packages/fil/src/types.ts +++ b/packages/fil/src/types.ts @@ -82,8 +82,6 @@ export interface DepositResponse { size: number type: string }> - /** Metadata for database insertion */ - depositMetadata: DepositMetadata } /** @@ -124,8 +122,6 @@ export interface VerifyPaymentArgs { transactionHash: string /** CID of the uploaded content */ cid: string - /** Deposit metadata from server response */ - depositMetadata: DepositMetadata } /** @@ -140,6 +136,8 @@ export interface VerifyPaymentResponse { transactionHash: string /** CID of the uploaded content */ cid: string + /** Gateway URL to access the file */ + url?: string } /** diff --git a/packages/sol/CHANGELOG.md b/packages/sol/CHANGELOG.md index 614621c..b349fc6 100644 --- a/packages/sol/CHANGELOG.md +++ b/packages/sol/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.0.0 + +### Major Changes + +- upload flow now pins to pinata at deposit time — the file upload step after confirm is no longer needed and has been removed + + breaking: deposit response no longer includes depositMetadata + breaking: confirm no longer accepts depositMetadata in the request body + breaking: url is now returned from the confirm/verify-payment response instead of the file upload response + +### Patch Changes + +- 38a0367: renewal response now returns the gateway url from the server instead of a hardcoded w3s.link url + ## 0.1.6 ### Patch Changes diff --git a/packages/sol/package.json b/packages/sol/package.json index bc6b985..b93c7bf 100644 --- a/packages/sol/package.json +++ b/packages/sol/package.json @@ -1,6 +1,6 @@ { "name": "@toju.network/sol", - "version": "0.1.6", + "version": "1.0.0", "type": "module", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/sol/src/payment.ts b/packages/sol/src/payment.ts index 35379c1..d61734b 100644 --- a/packages/sol/src/payment.ts +++ b/packages/sol/src/payment.ts @@ -40,10 +40,6 @@ export async function createDepositTxn( if (userEmail) formData.append('userEmail', userEmail) if (args.directoryName) formData.append('directoryName', args.directoryName) - const isMultipleFiles = file.length > 1 - - let _uploadErr - const depositReq = await fetch(`${apiEndpoint}/upload/deposit`, { method: 'POST', body: formData, @@ -94,7 +90,9 @@ export async function createDepositTxn( ) throw new Error( - 'Transaction failed during simulation. Please try again.', + logs.length + ? `Transaction simulation failed:\n${logs.join('\n')}` + : 'Transaction failed during simulation. Please try again.', ) } @@ -129,7 +127,6 @@ export async function createDepositTxn( body: JSON.stringify({ cid: depositRes.cid, transactionHash: signature, - depositMetadata: depositRes.depositMetadata, }), }) @@ -146,59 +143,14 @@ export async function createDepositTxn( ) } - if (depositRes.error) { - _uploadErr = depositRes.error - } - - const uploadForm = new FormData() - file.forEach((f) => uploadForm.append('file', f)) - - let fileUploadReq - // calls the upload functionality on our server with the file when deposit is successful - if (isMultipleFiles) { - fileUploadReq = await fetch( - `${apiEndpoint}/upload/files?cid=${encodeURIComponent(depositRes.cid)}`, - { - method: 'POST', - body: uploadForm, - }, - ) - } else { - fileUploadReq = await fetch( - `${apiEndpoint}/upload/file?cid=${encodeURIComponent(depositRes.cid)}`, - { - method: 'POST', - body: uploadForm, - }, - ) - } - - if (!fileUploadReq.ok) { - let err = 'Unknown error' - try { - const data: DepositResult = await fileUploadReq.json() - err = data.message || data.error || err - } catch {} - throw new Error('Deposit API error: ' + err) - } - - const fileUploadRes: Pick = - await fileUploadReq?.json() + const confirmData = await confirmRes.json().catch(() => ({})) return { signature: signature as Signature, success: true, cid: depositRes.cid, - url: fileUploadRes.object.url, - message: fileUploadRes.object.message, - fileInfo: fileUploadRes.object - ? { - filename: fileUploadRes.object.fileInfo?.filename || '', - size: fileUploadRes?.object?.fileInfo?.size || 0, - uploadedAt: fileUploadRes?.object?.fileInfo?.uploadedAt || '', - type: fileUploadRes?.object?.fileInfo?.type || '', - } - : undefined, + url: confirmData?.url || '', + message: confirmData?.message || '', } } catch (error) { console.error(error) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ab5c69..0d569ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,10 +44,10 @@ importers: dependencies: '@solana/kit': specifier: ^2.3.0 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@solana/web3.js': specifier: ^1.98.4 - version: 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10) + version: 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@6.0.6) devDependencies: knip: specifier: ^5.88.1 @@ -122,9 +122,6 @@ importers: '@x402/express': specifier: ^2.8.0 version: 2.9.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(express@5.2.1)(typescript@5.9.3)(utf-8-validate@6.0.6) - axios: - specifier: ^1.14.0 - version: 1.14.0 bn.js: specifier: ^5.2.3 version: 5.2.3 @@ -133,7 +130,7 @@ importers: version: 2.8.6 dotenv: specifier: ^17.3.1 - version: 17.4.0 + version: 17.4.1 drizzle-orm: specifier: ^0.44.7 version: 0.44.7(@neondatabase/serverless@1.0.2)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.20.0)(postgres@3.4.8) @@ -265,11 +262,11 @@ importers: specifier: ^1.167.9 version: 1.167.9(@tanstack/react-router@1.168.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@toju.network/fil': - specifier: ^0.2.2 - version: 0.2.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: ^1.0.0 + version: 1.0.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@toju.network/sol': - specifier: ^0.1.6 - version: 0.1.6(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + specifier: ^1.0.0 + version: 1.0.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) dayjs: specifier: ^1.11.20 version: 1.11.20 @@ -369,13 +366,13 @@ importers: version: 2.9.0(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6) dotenv: specifier: latest - version: 17.4.0 + version: 17.4.1 express: specifier: latest version: 5.2.1 viem: specifier: latest - version: 2.47.6(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@4.3.6) + version: 2.47.10(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@4.3.6) devDependencies: '@types/express': specifier: latest @@ -3723,11 +3720,11 @@ packages: engines: {node: '>=20.19'} hasBin: true - '@toju.network/fil@0.2.2': - resolution: {integrity: sha512-m7pmCI/IftEFZat4qbwTTFzcMswbjANErh55U5j4xtjalHI0qYNRgb73fk0G87RMLOxF01NMmPVuVGdhk0sRGg==} + '@toju.network/fil@1.0.0': + resolution: {integrity: sha512-LeiHr9oL3vSLKeQzFocjaRG+QEkI8t7Jqz3xkark1vchOUAo06M3BKKgASeph+DBC/Z9tQvG7oIE1rRJUh9tdQ==} - '@toju.network/sol@0.1.6': - resolution: {integrity: sha512-lpq4neIS7Lw8oQN+IMUhcNtJxwYhCaAwHEIz4tT9lGWmsW4PEiSSPICWxDcfHXdNh37C1Bfrnr3Cgrx6RX22ug==} + '@toju.network/sol@1.0.0': + resolution: {integrity: sha512-hbsHVkNhcLxUMMCswYsO/Uuah/NQ3aIZjgS32ClYJGiqfS81BuDPz+nSBg//yewl1D91wSaIuPRll+zcM4rz+g==} '@toruslabs/base-controllers@5.11.0': resolution: {integrity: sha512-5AsGOlpf3DRIsd6PzEemBoRq+o2OhgSFXj5LZD6gXcBlfe0OpF+ydJb7Q8rIt5wwpQLNJCs8psBUbqIv7ukD2w==} @@ -5414,8 +5411,8 @@ packages: resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} engines: {node: '>=10'} - dotenv@17.4.0: - resolution: {integrity: sha512-kCKF62fwtzwYm0IGBNjRUjtJgMfGapII+FslMHIjMR5KTnwEmBmWLDRSnc3XSNP8bNy34tekgQyDT0hr7pERRQ==} + dotenv@17.4.1: + resolution: {integrity: sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==} engines: {node: '>=12'} draggabilly@3.0.0: @@ -9151,6 +9148,14 @@ packages: typescript: optional: true + viem@2.47.10: + resolution: {integrity: sha512-D+l6SDDZWB5bh8u9hgICzMX2/egMrgEQ+Pef/QkZgmOl6bOTyCQMSgWAH8jZTWJ/218J9QNv7s/9BH6Wu5oPDg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + viem@2.47.6: resolution: {integrity: sha512-zExmbI99NGvMdYa7fmqSTLgkwh48dmhgEqFrUgkpL4kfG4XkVefZ8dZqIKVUhZo6Uhf0FrrEXOsHm9LUyIvI2Q==} peerDependencies: @@ -9993,7 +9998,7 @@ snapshots: jose: 6.2.2 md5: 2.3.0 uncrypto: 0.1.3 - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -10639,7 +10644,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.12.0 + '@types/node': 25.5.2 jest-mock: 29.7.0 '@jest/environment@30.3.0': @@ -10664,7 +10669,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 24.12.0 + '@types/node': 25.5.2 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -11615,7 +11620,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) transitivePeerDependencies: - bufferutil - typescript @@ -11626,7 +11631,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript @@ -11639,7 +11644,7 @@ snapshots: '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(utf-8-validate@5.0.10)))(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11753,7 +11758,7 @@ snapshots: '@walletconnect/logger': 2.1.2 '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(utf-8-validate@5.0.10)))(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11806,7 +11811,7 @@ snapshots: '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(utf-8-validate@5.0.10)))(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12398,7 +12403,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12411,11 +12416,11 @@ snapshots: '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) typescript: 5.9.3 @@ -12423,7 +12428,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12436,11 +12441,11 @@ snapshots: '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) typescript: 5.9.3 @@ -12645,23 +12650,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.3) '@solana/functional': 2.3.0(typescript@5.9.3) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) '@solana/subscribable': 2.3.0(typescript@5.9.3) typescript: 5.9.3 - ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.3) '@solana/functional': 2.3.0(typescript@5.9.3) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) '@solana/subscribable': 2.3.0(typescript@5.9.3) typescript: 5.9.3 - ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: @@ -12693,7 +12698,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.3) '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.3) @@ -12701,7 +12706,7 @@ snapshots: '@solana/promises': 2.3.0(typescript@5.9.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12711,7 +12716,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.3) '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.3) @@ -12719,7 +12724,7 @@ snapshots: '@solana/promises': 2.3.0(typescript@5.9.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.3) '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.3) '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12907,7 +12912,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12915,7 +12920,7 @@ snapshots: '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/promises': 2.3.0(typescript@5.9.3) '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12924,7 +12929,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -12932,7 +12937,7 @@ snapshots: '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/promises': 2.3.0(typescript@5.9.3) '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -13808,14 +13813,14 @@ snapshots: '@tanstack/virtual-file-routes@1.161.7': {} - '@toju.network/fil@0.2.2(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@toju.network/fil@1.0.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: ethers: 6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate - '@toju.network/sol@0.1.6(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@toju.network/sol@1.0.0(bufferutil@4.1.0)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10) @@ -15283,7 +15288,7 @@ snapshots: jose: 5.10.0 siwe: 2.3.2(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) tweetnacl: 1.0.3 - viem: 2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -15300,7 +15305,7 @@ snapshots: jose: 5.10.0 siwe: 2.3.2(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) tweetnacl: 1.0.3 - viem: 2.47.6(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.45.3(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -15965,7 +15970,7 @@ snapshots: chrome-launcher@0.15.2: dependencies: - '@types/node': 24.12.0 + '@types/node': 25.5.2 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -15974,7 +15979,7 @@ snapshots: chromium-edge-launcher@0.2.0: dependencies: - '@types/node': 24.12.0 + '@types/node': 25.5.2 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -16332,7 +16337,7 @@ snapshots: domain-browser@4.22.0: {} - dotenv@17.4.0: {} + dotenv@17.4.1: {} draggabilly@3.0.0: dependencies: @@ -17777,7 +17782,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.12.0 + '@types/node': 25.5.2 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -17863,7 +17868,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 24.12.0 + '@types/node': 25.5.2 jest-util: 29.7.0 jest-mock@30.3.0: @@ -19064,7 +19069,7 @@ snapshots: outdent@0.5.0: {} - ox@0.12.1(typescript@5.9.3)(zod@3.25.76): + ox@0.12.1(typescript@5.9.3)(zod@3.22.4): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -19072,14 +19077,14 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - zod - ox@0.12.1(typescript@5.9.3)(zod@4.3.6): + ox@0.12.1(typescript@5.9.3)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -19087,14 +19092,14 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - zod - ox@0.12.1(typescript@6.0.2)(zod@3.25.76): + ox@0.12.1(typescript@5.9.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -19102,14 +19107,14 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@6.0.2)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) eventemitter3: 5.0.1 optionalDependencies: - typescript: 6.0.2 + typescript: 5.9.3 transitivePeerDependencies: - zod - ox@0.14.7(typescript@5.9.3)(zod@3.22.4): + ox@0.12.1(typescript@6.0.2)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -19117,10 +19122,10 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) + abitype: 1.2.3(typescript@6.0.2)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.2 transitivePeerDependencies: - zod @@ -20956,6 +20961,23 @@ snapshots: - utf-8-validate - zod + viem@2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.12.1(typescript@5.9.3)(zod@3.22.4) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + viem@2.45.3(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 @@ -21024,35 +21046,18 @@ snapshots: - utf-8-validate - zod - viem@2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4): - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) - isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - ox: 0.14.7(typescript@5.9.3)(zod@3.22.4) - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - - viem@2.47.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.47.10(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@4.3.6): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) - isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - ox: 0.14.7(typescript@5.9.3)(zod@3.25.76) - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + abitype: 1.2.3(typescript@6.0.2)(zod@4.3.6) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + ox: 0.14.7(typescript@6.0.2)(zod@4.3.6) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.2 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -21092,23 +21097,6 @@ snapshots: - utf-8-validate - zod - viem@2.47.6(bufferutil@4.1.0)(typescript@6.0.2)(utf-8-validate@6.0.6)(zod@4.3.6): - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@6.0.2)(zod@4.3.6) - isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - ox: 0.14.7(typescript@6.0.2)(zod@4.3.6) - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) - optionalDependencies: - typescript: 6.0.2 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - vite-node@3.2.4(@types/node@22.19.15)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: cac: 6.7.14 diff --git a/server/package.json b/server/package.json index 2103282..aefb633 100644 --- a/server/package.json +++ b/server/package.json @@ -48,7 +48,6 @@ "@x402/core": "^2.8.0", "@x402/evm": "^2.8.0", "@x402/express": "^2.8.0", - "axios": "^1.14.0", "bn.js": "^5.2.3", "cors": "^2.8.6", "dotenv": "^17.3.1", diff --git a/server/src/controllers/agent.controller.ts b/server/src/controllers/agent.controller.ts index 0f4dc26..da9a379 100644 --- a/server/src/controllers/agent.controller.ts +++ b/server/src/controllers/agent.controller.ts @@ -3,8 +3,8 @@ import { eq } from 'drizzle-orm' import { Request, Response } from 'express' import { db } from '../db/db.js' import { uploads } from '../db/schema.js' -import { pinCAR } from '../services/storage/pinata.service.js' -import { buildCAR } from '../utils/compute-cid.js' +import { pinFiles } from '../services/storage/pinata.service.js' +import { computeCID } from '../utils/compute-cid.js' import { getAmountInUSD } from '../utils/constant.js' import { getExpiryDate } from '../utils/functions.js' import { logger } from '../utils/logger.js' @@ -45,7 +45,7 @@ export const uploadAgentFile = async (req: Request, res: Response) => { [file.originalname]: new Uint8Array(file.buffer), } - const { cid: computedCID, carBuffer } = await buildCAR(fileMap) + const computedCID = await computeCID(fileMap) const existing = await db .select() @@ -60,10 +60,18 @@ export const uploadAgentFile = async (req: Request, res: Response) => { expiresAt: existing[0].expiresAt, }) - const pinnedCID = await pinCAR(carBuffer, file.originalname) + const pinnedCID = await pinFiles( + { + [file.originalname]: { + buffer: new Uint8Array(file.buffer), + mimetype: file.mimetype, + }, + }, + file.originalname, + ) if (pinnedCID !== computedCID) - logger.warn('CID version mismatch between computed and pinned', { + logger.warn('CID mismatch between pre-computed and pinned', { computed: computedCID, pinned: pinnedCID, }) diff --git a/server/src/controllers/jobs.controller.ts b/server/src/controllers/jobs.controller.ts index e741bbd..842dfd1 100644 --- a/server/src/controllers/jobs.controller.ts +++ b/server/src/controllers/jobs.controller.ts @@ -1,6 +1,10 @@ +import { eq } from 'drizzle-orm' import { Request, Response } from 'express' +import { db } from '../db/db.js' +import { uploads } from '../db/schema.js' import { batchUpdateWarningSentAt, + getAbandonedPendingUploads, getExpiredDeposits, getUploadsGroupedByEmail, updateDeletionStatus, @@ -161,6 +165,56 @@ export const deleteExpiredUploads = async (_req: Request, res: Response) => { } } +/** + * Unpins and removes pending uploads that were never paid for. + * Runs daily — cleans up files pinned during deposit but abandoned before payment. + */ +export const deleteAbandonedUploads = async (_req: Request, res: Response) => { + try { + logger.info('Running abandoned uploads cleanup job') + const abandoned = await getAbandonedPendingUploads(24) + + if (!abandoned || abandoned.length === 0) { + logger.info('No abandoned uploads to clean up') + return res + .status(200) + .json({ success: true, message: 'No abandoned uploads to clean up' }) + } + + logger.info('Found abandoned uploads to clean up', { + count: abandoned.length, + }) + + for (const record of abandoned) { + try { + await unpinCID(record.contentCid) + await db.delete(uploads).where(eq(uploads.id, record.id)) + logger.info('Removed abandoned upload', { + id: record.id, + cid: record.contentCid, + }) + } catch (error) { + logger.error('Failed to remove abandoned upload', { + id: record.id, + cid: record.contentCid, + error: error instanceof Error ? error.message : String(error), + }) + } + } + + return res + .status(200) + .json({ success: true, message: 'Abandoned uploads cleaned up' }) + } catch (error) { + logger.error('Error in deleteAbandonedUploads job', { + error: error instanceof Error ? error.message : String(error), + }) + return res + .status(500) + .json({ success: false, message: 'Failed to clean up abandoned uploads' }) + } +} + /** * daily usage snapshot job * runs every day at 9am UTC diff --git a/server/src/controllers/upload.controller.ts b/server/src/controllers/upload.controller.ts index db64d31..400d903 100644 --- a/server/src/controllers/upload.controller.ts +++ b/server/src/controllers/upload.controller.ts @@ -11,7 +11,6 @@ import { } from '../services/fil/verify.service.js' import { getSolPrice } from '../services/price/sol-price.service.js' import { PaginationContext } from '../types.js' -import { buildCAR, computeCID } from '../utils/compute-cid.js' import { DAY_TIME_IN_SECONDS, getAmountInLamportsFromUSD, @@ -19,7 +18,7 @@ import { import { getExpiryDate, getPaginationParams } from '../utils/functions.js' import { logger } from '../utils/logger.js' import { getPricingConfig } from '../utils/pricing.js' -import { gatewayUrl, pinCAR } from '../services/storage/pinata.service.js' +import { gatewayUrl, pinFiles } from '../services/storage/pinata.service.js' import { createDepositTransaction } from './solana.controller.js' const MIN_DURATION_SECONDS = DAY_TIME_IN_SECONDS // 1 day @@ -38,26 +37,25 @@ export const uploadFile = async (req: Request, res: Response) => { const cid = req.query.cid as string if (!cid) return res.status(400).json({ message: 'CID is required' }) - const fileMap = { [file.originalname]: new Uint8Array(file.buffer) } - const { cid: computedCID, carBuffer } = await buildCAR(fileMap) - - if (computedCID !== cid) { - throw new Error( - `CID mismatch! Precomputed: ${cid}, Built: ${computedCID}`, - ) - } - - const pinnedCID = await pinCAR(carBuffer, file.originalname) + const pinnedCID = await pinFiles( + { + [file.originalname]: { + buffer: new Uint8Array(file.buffer), + mimetype: file.mimetype, + }, + }, + file.originalname, + ) - if (pinnedCID !== computedCID) { - logger.warn('CID version mismatch between computed and pinned', { - computed: computedCID, + if (pinnedCID !== cid) { + logger.warn('CID mismatch between pre-computed and pinned', { + precomputed: cid, pinned: pinnedCID, }) } Sentry.setContext('file-upload', { - cid: computedCID, + cid: cid, fileName: file.originalname, fileSize: file.size, mimeType: file.mimetype, @@ -65,13 +63,13 @@ export const uploadFile = async (req: Request, res: Response) => { res.status(200).json({ message: 'Upload successful', - cid: computedCID, + cid: cid, object: { - cid: computedCID, + cid: cid, filename: file.originalname, size: file.size, type: file.mimetype, - url: gatewayUrl(computedCID, file.originalname), + url: gatewayUrl(cid, file.originalname), uploadedAt: new Date().toISOString(), }, }) @@ -101,29 +99,27 @@ export const uploadFiles = async (req: Request, res: Response) => { const cid = req.query.cid as string if (!cid) return res.status(400).json({ message: 'CID is required' }) - const fileMap: Record = {} + const fileMap: Record = {} for (const f of files) { - fileMap[f.originalname] = new Uint8Array(f.buffer) + fileMap[f.originalname] = { + buffer: new Uint8Array(f.buffer), + mimetype: f.mimetype, + } } - const { cid: computedCID, carBuffer } = await buildCAR(fileMap) - - if (computedCID !== cid) - throw new Error(`CID mismatch! Computed: ${cid}, Built: ${computedCID}`) - - const pinnedCID = await pinCAR( - carBuffer, + const pinnedCID = await pinFiles( + fileMap, `directory-${crypto.randomUUID()}`, ) - if (pinnedCID !== computedCID) - logger.warn('CID version mismatch between computed and pinned', { - computed: computedCID, + if (pinnedCID !== cid) + logger.warn('CID mismatch between pre-computed and pinned', { + precomputed: cid, pinned: pinnedCID, }) Sentry.setContext('multi-file-upload', { - cid: computedCID, + cid, fileSize: files?.reduce((acc, curr) => acc + curr.size, 0), fileNames: files.map((f) => f.originalname), mimeTypes: files.map((f) => f.mimetype), @@ -132,16 +128,16 @@ export const uploadFiles = async (req: Request, res: Response) => { res.status(200).json({ message: 'Upload successful', - cid: computedCID, + cid, object: { - cid: computedCID, - url: gatewayUrl(computedCID), + cid, + url: gatewayUrl(cid), size: files.reduce((sum, f) => sum + f.size, 0), files: files.map((f) => ({ filename: f.originalname, size: f.size, type: f.mimetype, - url: gatewayUrl(computedCID, f.originalname), + url: gatewayUrl(cid, f.originalname), })), uploadedAt: new Date().toISOString(), }, @@ -219,12 +215,17 @@ export const deposit = async (req: Request, res: Response) => { amountInLamports, }) - const computedCID = await computeCID(fileMap) + const pinnedCID = await pinFiles( + fileMap, + fileArray.length === 1 + ? fileArray[0].originalname + : directoryName || `dir-${Date.now()}`, + ) const existingUpload = await db .select() .from(uploads) - .where(eq(uploads.contentCid, computedCID)) + .where(eq(uploads.contentCid, pinnedCID)) .limit(1) if (existingUpload.length > 0 && existingUpload[0].transactionHash) @@ -238,7 +239,7 @@ export const deposit = async (req: Request, res: Response) => { totalSize, fileCount: fileArray.length, duration: duration_days, - cid: computedCID, + cid: pinnedCID, }) Sentry.setTag('operation', 'deposit') @@ -248,46 +249,69 @@ export const deposit = async (req: Request, res: Response) => { throw new Error(`Invalid deposit amount calculated: ${amountInLamports}`) } + const expiresAt = getExpiryDate(duration_days) + const fileName = + fileArray.length === 1 ? fileArray[0].originalname : directoryName || null + const fileType = + fileArray.length === 1 ? fileArray[0].mimetype : 'directory' + + if (existingUpload.length === 0) { + await db.insert(uploads).values({ + depositAmount: amountInLamports, + durationDays: duration_days, + contentCid: pinnedCID, + depositKey: publicKey, + depositSlot: 1, + lastClaimedSlot: 1, + expiresAt, + createdAt: new Date().toISOString(), + userEmail: sanitizedEmail || null, + fileName, + fileType, + fileSize: totalSize, + transactionHash: null, + deletionStatus: 'pending', + warningSentAt: null, + paymentChain: 'sol', + paymentToken: 'SOL', + }) + } else { + // pending record exists — refresh metadata in case user retries with updated params + await db + .update(uploads) + .set({ + depositAmount: amountInLamports, + durationDays: duration_days, + depositKey: publicKey, + expiresAt, + userEmail: sanitizedEmail || null, + fileName, + fileType, + fileSize: totalSize, + deletionStatus: 'pending', + }) + .where(eq(uploads.contentCid, pinnedCID)) + } + const depositInstructions = await createDepositTransaction({ publicKey, fileSize: totalSize, - contentCID: computedCID, + contentCID: pinnedCID, durationDays: duration_days, depositAmount: amountInLamports, }) - const backupExpirationDate = getExpiryDate(duration_days) - - // pass deposit meta later in the upload flow for db writes - // after a succesful confirmation - const depositMetadata = { - depositAmount: amountInLamports, - durationDays: duration_days, - depositKey: publicKey, - userEmail: sanitizedEmail || null, - fileName: - fileArray.length === 1 - ? fileArray[0].originalname - : directoryName || null, - fileType: fileArray.length === 1 ? fileArray[0].mimetype : 'directory', - fileSize: totalSize, - expiresAt: backupExpirationDate, - paymentChain: 'sol', - paymentToken: 'SOL', - } - res.status(200).json({ message: 'Deposit instruction ready — sign to finalize upload', - cid: computedCID, + cid: pinnedCID, instructions: depositInstructions, fileCount: fileArray.length, - totalSize: totalSize, + totalSize, files: fileArray.map((f) => ({ name: f.originalname, size: f.size, type: f.mimetype, })), - depositMetadata, }) } catch (error) { Sentry.captureException(error) @@ -304,7 +328,7 @@ export const deposit = async (req: Request, res: Response) => { type FileMeta = { totalSize: number fileArray: Express.Multer.File[] - fileMap: Record + fileMap: Record } /** @@ -338,11 +362,14 @@ const fileBuilder = ( if (fileArray.length === 0) throw new Error('No files selected') - const fileMap: Record = {} + const fileMap: Record = {} let totalSize = 0 for (const file of fileArray) { - fileMap[file.originalname] = new Uint8Array(file.buffer) + fileMap[file.originalname] = { + buffer: new Uint8Array(file.buffer), + mimetype: file.mimetype, + } totalSize += file.size } @@ -389,12 +416,25 @@ export const depositUsdFC = async (req: Request, res: Response) => { amountInUSDFC: amountInUSDFC.toString(), }) - const computedCID = await computeCID(fileMap) + // this is a reference to what i've seen in the filecoin-pin repo. + // javascript has another number type, apparently — BigNum/Int + if (amountInUSDFC <= 0n) + throw new Error(`Invalid deposit amount calculated: ${amountInUSDFC}`) + + const durationNum = Number(duration) + if (!Number.isFinite(durationNum)) throw new Error('Invalid duration') + + const pinnedCID = await pinFiles( + fileMap, + fileArray.length === 1 + ? fileArray[0].originalname + : directoryName || `dir-${Date.now()}`, + ) const existingUpload = await db .select() .from(uploads) - .where(eq(uploads.contentCid, computedCID)) + .where(eq(uploads.contentCid, pinnedCID)) .limit(1) if (existingUpload.length > 0 && existingUpload[0].transactionHash) @@ -408,7 +448,7 @@ export const depositUsdFC = async (req: Request, res: Response) => { totalSize, fileCount: fileArray.length, duration: duration_days, - cid: computedCID, + cid: pinnedCID, chain: 'FIL', }) @@ -416,30 +456,48 @@ export const depositUsdFC = async (req: Request, res: Response) => { Sentry.setTag('file_count', fileArray.length) Sentry.setTag('payment_chain', 'fil') - // this is a reference to what i've seen in the filecoin-pin repo. - // javascript has another number type, apparently — BigNum/Int - if (amountInUSDFC <= 0n) - throw new Error(`Invalid deposit amount calculated: ${amountInUSDFC}`) - - const durationNum = Number(duration) - if (!Number.isFinite(durationNum)) throw new Error('Invalid duration') - - const backupExpirationDate = getExpiryDate(duration_days) - - const depositMetadata = { - depositAmount: amountInUSDFC.toString(), - durationDays: duration_days, - depositKey: userAddress, - userEmail: userEmail || null, - fileName: - fileArray.length === 1 - ? fileArray[0].originalname - : directoryName || null, - fileType: fileArray.length === 1 ? fileArray[0].mimetype : 'directory', - fileSize: totalSize, - expiresAt: backupExpirationDate, - paymentChain: 'fil', - paymentToken: 'USDFC', + const expiresAt = getExpiryDate(duration_days) + const fileName = + fileArray.length === 1 ? fileArray[0].originalname : directoryName || null + const fileType = + fileArray.length === 1 ? fileArray[0].mimetype : 'directory' + + if (existingUpload.length === 0) { + await db.insert(uploads).values({ + depositAmount: Number(amountInUSDFC), + durationDays: duration_days, + contentCid: pinnedCID, + depositKey: userAddress, + depositSlot: 0, + lastClaimedSlot: 0, + expiresAt, + createdAt: new Date().toISOString(), + userEmail: userEmail || null, + fileName, + fileType, + fileSize: totalSize, + transactionHash: null, + deletionStatus: 'pending', + warningSentAt: null, + paymentChain: 'fil', + paymentToken: 'USDFC', + }) + } else { + // pending record exists — refresh metadata in case user retries with updated params + await db + .update(uploads) + .set({ + depositAmount: Number(amountInUSDFC), + durationDays: duration_days, + depositKey: userAddress, + expiresAt, + userEmail: userEmail || null, + fileName, + fileType, + fileSize: totalSize, + deletionStatus: 'pending', + }) + .where(eq(uploads.contentCid, pinnedCID)) } const isMainnet = process.env.NODE_ENV === 'production' @@ -449,18 +507,17 @@ export const depositUsdFC = async (req: Request, res: Response) => { res.status(200).json({ message: 'Payment details ready — transfer USDFC to proceed with upload', - cid: computedCID, + cid: pinnedCID, amountUSDFC: amountInUSDFC.toString(), recipientAddress: config[0].filecoinWallet, usdfcContractAddress, fileCount: fileArray.length, - totalSize: totalSize, + totalSize, files: fileArray.map((f) => ({ name: f.originalname, size: f.size, type: f.mimetype, })), - depositMetadata, }) } catch (error) { Sentry.captureException(error) @@ -518,11 +575,12 @@ export const getUploadHistory = async (req: Request, res: Response) => { } /** - * Function to create DB record and save transaction hash after payment is confirmed + * Marks a pending upload as confirmed after the Solana transaction is verified. + * The file is already pinned on Pinata from the deposit step. */ export const confirmUpload = async (req: Request, res: Response) => { try { - const { cid, transactionHash, depositMetadata } = req.body + const { cid, transactionHash } = req.body if (!cid || !transactionHash) { return res.status(400).json({ @@ -530,83 +588,50 @@ export const confirmUpload = async (req: Request, res: Response) => { }) } - if (!depositMetadata) { - return res.status(400).json({ - message: 'Deposit metadata is required', - }) - } - const existing = await db .select() .from(uploads) .where(eq(uploads.contentCid, cid)) .limit(1) - if (existing.length > 0) { - // exists but has no transaction hash, update it - if (!existing[0].transactionHash) { - const updated = await db - .update(uploads) - .set({ transactionHash: transactionHash }) - .where(eq(uploads.contentCid, cid)) - .returning() - - await saveTransaction({ - depositId: updated[0].id, - contentCid: cid, - transactionHash: transactionHash, - transactionType: 'initial_deposit', - amountInLamports: updated[0].depositAmount, - durationDays: updated[0].durationDays, - }) - - return res.status(200).json({ - verified: true, - message: 'Transaction hash updated successfully', - deposit: updated[0], - }) - } + if (existing.length === 0) + return res.status(404).json({ + message: 'No pending upload found for this CID', + }) + if (existing[0].transactionHash) return res.status(409).json({ message: 'This upload has already been confirmed', deposit: existing[0], }) - } - - const depositItem: typeof uploads.$inferInsert = { - depositAmount: depositMetadata.depositAmount, - durationDays: depositMetadata.durationDays, - contentCid: cid, - depositKey: depositMetadata.depositKey, - depositSlot: 1, - lastClaimedSlot: 1, - expiresAt: depositMetadata.expiresAt, - createdAt: new Date().toISOString(), - userEmail: depositMetadata.userEmail, - fileName: depositMetadata.fileName, - fileType: depositMetadata.fileType, - fileSize: depositMetadata.fileSize, - transactionHash: transactionHash, - deletionStatus: 'active', - warningSentAt: null, - paymentChain: depositMetadata.paymentChain || 'sol', - paymentToken: depositMetadata.paymentToken || 'SOL', - } - const inserted = await db.insert(uploads).values(depositItem).returning() + const [confirmedUpload] = await db + .update(uploads) + .set({ transactionHash, deletionStatus: 'active' }) + .where(eq(uploads.contentCid, cid)) + .returning() await saveTransaction({ - depositId: inserted[0].id, + depositId: confirmedUpload.id, contentCid: cid, - transactionHash: transactionHash, + transactionHash, transactionType: 'initial_deposit', - amountInLamports: inserted[0].depositAmount, - durationDays: inserted[0].durationDays, + amountInLamports: confirmedUpload.depositAmount, + durationDays: confirmedUpload.durationDays, }) + const url = gatewayUrl( + cid, + confirmedUpload.fileType === 'directory' + ? undefined + : (confirmedUpload.fileName ?? undefined), + ) + return res.status(200).json({ - message: 'Upload confirmed and saved successfully', - deposit: inserted[0], + verified: true, + message: 'Upload confirmed successfully', + deposit: confirmedUpload, + url, }) } catch (err) { Sentry.captureException(err) @@ -625,25 +650,24 @@ export const confirmUpload = async (req: Request, res: Response) => { * * @param req.body.cid - Content identifier of the uploaded files * @param req.body.transactionHash - Filecoin transaction hash of the USDFC transfer - * @param req.body.depositMetadata - Metadata from depositUsdFC response * @returns Confirmation message with deposit record * * @remarks * Transaction verification will be implemented with indexer (see #176). * SDK handles file pinning to IPFS via Pinata via /upload/file(s) endpoints. */ +/** + * Verifies USDFC payment transaction and marks the pending upload as confirmed. + * The file is already pinned on Pinata from the deposit step. + */ export const verifyUsdFcPayment = async (req: Request, res: Response) => { try { - const { cid, transactionHash, depositMetadata } = req.body + const { cid, transactionHash } = req.body if (!cid || !transactionHash) return res.status(400).json({ message: 'The CID and transaction hash are required', }) - if (!depositMetadata) - return res.status(400).json({ - message: 'Deposit metadata for this USDFC transaction is required', - }) const existing = await db .select() @@ -651,96 +675,54 @@ export const verifyUsdFcPayment = async (req: Request, res: Response) => { .where(eq(uploads.contentCid, cid)) .limit(1) - if (existing.length > 0) { - if (!existing[0].transactionHash) { - const updated = await db - .update(uploads) - .set({ transactionHash: transactionHash }) - .where(eq(uploads.contentCid, cid)) - .returning() - - await saveTransaction({ - depositId: updated[0].id, - contentCid: cid, - transactionHash: transactionHash, - transactionType: 'initial_deposit', - amountInLamports: updated[0].depositAmount, - durationDays: updated[0].durationDays, - }) - - return res.status(200).json({ - message: 'Transaction hash updated successfully', - deposit: updated[0], - }) - } + if (existing.length === 0) + return res.status(404).json({ + message: 'No pending upload found for this CID', + }) + if (existing[0].transactionHash) return res.status(409).json({ message: 'This upload has already been confirmed', deposit: existing[0], }) - } const config = await db.select().from(configTable) if (!config[0].filecoinWallet) throw new Error('Filecoin wallet not configured') - const treasury = config[0].filecoinWallet - const userAddress = depositMetadata.depositKey - - const USDFC_CONTRACT = getUsdfcContractAddress() - - const expectedAmount = BigInt(depositMetadata.depositAmount) - const { verified } = await verifyErc20Transfer({ transactionHash, - from: userAddress, - to: treasury, - contractAddress: USDFC_CONTRACT, - expectedAmount, + from: existing[0].depositKey, + to: config[0].filecoinWallet, + contractAddress: getUsdfcContractAddress(), + expectedAmount: BigInt(existing[0].depositAmount), }) - if (!verified) { + if (!verified) return res.status(400).json({ message: 'USDFC transfer verification failed. Transaction may not exist, may have failed, or the amount/recipient does not match.', }) - } - const depositItem: typeof uploads.$inferInsert = { - depositAmount: depositMetadata.depositAmount, - durationDays: depositMetadata.durationDays, - contentCid: cid, - depositKey: depositMetadata.depositKey, - depositSlot: 0, - lastClaimedSlot: 0, - expiresAt: depositMetadata.expiresAt, - createdAt: new Date().toISOString(), - userEmail: depositMetadata.userEmail, - fileName: depositMetadata.fileName, - fileType: depositMetadata.fileType, - fileSize: depositMetadata.fileSize, - transactionHash: transactionHash, - deletionStatus: 'active', - warningSentAt: null, - paymentChain: 'fil', - paymentToken: 'USDFC', - } - - const inserted = await db.insert(uploads).values(depositItem).returning() + const [confirmedUpload] = await db + .update(uploads) + .set({ transactionHash, deletionStatus: 'active' }) + .where(eq(uploads.contentCid, cid)) + .returning() await saveTransaction({ - depositId: inserted[0].id, + depositId: confirmedUpload.id, contentCid: cid, - transactionHash: transactionHash, + transactionHash, transactionType: 'initial_deposit', - amountInLamports: Number(depositMetadata.depositAmount), - durationDays: inserted[0].durationDays, + amountInLamports: confirmedUpload.depositAmount, + durationDays: confirmedUpload.durationDays, }) return res.status(200).json({ verified: true, message: 'USDFC payment verified and upload confirmed successfully', - deposit: inserted[0], + deposit: confirmedUpload, }) } catch (error) { Sentry.captureException(error) diff --git a/server/src/db/uploads.ts b/server/src/db/uploads.ts index a7c8557..bb61941 100644 --- a/server/src/db/uploads.ts +++ b/server/src/db/uploads.ts @@ -1,4 +1,4 @@ -import { and, desc, eq, inArray, isNull, lte, or, sql } from 'drizzle-orm' +import { and, desc, eq, inArray, isNull, lt, lte, or, sql } from 'drizzle-orm' import { gatewayUrl } from '../services/storage/pinata.service.js' import { PaginationContext } from '../types.js' import { logger } from '../utils/logger.js' @@ -280,6 +280,39 @@ export const renewStorageDuration = async (cid: string, duration: number) => { } } +/** + * Find pending uploads older than the given age in hours. + * These are files pinned to Pinata but never paid for — safe to unpin and remove. + * @param abandonedAfterHowManyHours - Age threshold in hours (default: 24) + */ +export const getAbandonedPendingUploads = async ( + abandonedAfterHowManyHours: number = 24, +) => { + try { + const cutoff = new Date( + Date.now() - abandonedAfterHowManyHours * 60 * 60 * 1000, + ).toISOString() + + const records = await db + .select() + .from(uploads) + .where( + and( + eq(uploads.deletionStatus, 'pending'), + isNull(uploads.transactionHash), + lt(uploads.createdAt, cutoff), + ), + ) + + return records + } catch (err) { + logger.error('Error getting abandoned pending uploads', { + error: err instanceof Error ? err.message : String(err), + }) + return null + } +} + /** * Save a transaction for an upload */ diff --git a/server/src/routes/jobs.route.ts b/server/src/routes/jobs.route.ts index 88d440d..ad251e2 100644 --- a/server/src/routes/jobs.route.ts +++ b/server/src/routes/jobs.route.ts @@ -16,6 +16,12 @@ jobs.post( jobsController.deleteExpiredUploads, ) +jobs.post( + '/remove-debtors', + verifyQStashRequest, + jobsController.deleteAbandonedUploads, +) + jobs.post( '/usage/snapshot', verifyQStashRequest, diff --git a/server/src/services/storage/pinata.service.ts b/server/src/services/storage/pinata.service.ts index 9013477..97362f6 100644 --- a/server/src/services/storage/pinata.service.ts +++ b/server/src/services/storage/pinata.service.ts @@ -14,24 +14,27 @@ function getClient(): PinataSDK { export function gatewayUrl(cid: string, filename?: string): string { const base = process.env.PINATA_GATEWAY if (!base) throw new Error('PINATA_GATEWAY env var not set') - return filename ? `${base}/ipfs/${cid}/${filename}` : `${base}/ipfs/${cid}` + return filename + ? `${base}/ipfs/${cid}/${encodeURIComponent(filename)}` + : `${base}/ipfs/${cid}` } /** - * Pins a CAR buffer to Pinata. - * The .car() builder flag tells Pinata to treat the upload as a CAR - * and use its root CID as the pin hash — so the returned CID matches - * our pre-computed CID from buildCAR. + * Pins one or more files as a directory to Pinata. + * Always uses fileArray so the returned CID is a directory CID, + * matching what computeCID produces via createDirectoryEncoderStream. */ -export async function pinCAR( - carBuffer: Uint8Array, - name: string, +export async function pinFiles( + fileMap: Record, + directoryName: string, ): Promise { const pinata = getClient() - const blob = new Blob([carBuffer], { type: 'application/vnd.ipld.car' }) - const file = new File([blob], `${name}.car`) - const result = await pinata.upload.public.file(file).name(name).car() - logger.info('pinata: CAR pinned', { cid: result.cid, name }) + const files = Object.entries(fileMap).map( + ([name, { buffer, mimetype }]) => + new File([buffer], name, { type: mimetype }), + ) + const result = await pinata.upload.public.fileArray(files).name(directoryName) + logger.info('pinata: directory pinned', { cid: result.cid, directoryName }) return result.cid } diff --git a/server/src/tests/Admin/updateDuration.test.ts b/server/src/tests/Admin/updateDuration.test.ts deleted file mode 100644 index 79ba64f..0000000 --- a/server/src/tests/Admin/updateDuration.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import axios from 'axios' -import dotenv from 'dotenv' - -dotenv.config() - -const BASE_URL = `http://localhost:${process.env.PORT}/api/admin` -test('Update the Minimum Duration of the File Upload can be set by admin only', async () => { - const response = await axios.post( - `${BASE_URL}/updateMinDuration`, - { - duration: 30, // this should be in number of days - }, - { - headers: { - 'x-api-key': process.env.ADMIN_API_KEY, // Must match process.env.ADMIN_API_KEY - }, - }, - ) - // expect(typeof response.data.message).toBe(string); - expect(response.data.message).toBe( - 'Successfully updated the minimum Duration', - ) -}) - -test('Update the Rate for File Upload, can be set by admin only', async () => { - const result = await axios.post( - `${BASE_URL}/updateRate`, - { - rate: 40, - }, - { - headers: { - 'x-api-key': process.env.ADMIN_API_KEY, // Must match process.env.ADMIN_API_KEY - }, - }, - ) - expect(result.data.message).toBe('Successfully updated the rate per file') -}) diff --git a/server/src/utils/compute-cid.ts b/server/src/utils/compute-cid.ts index cf1a152..b5c31a6 100644 --- a/server/src/utils/compute-cid.ts +++ b/server/src/utils/compute-cid.ts @@ -1,75 +1,44 @@ -import { - CAREncoderStream, - createDirectoryEncoderStream, - createFileEncoderStream, -} from 'ipfs-car' +import { CAREncoderStream, createDirectoryEncoderStream } from 'ipfs-car' import { logger } from './logger.js' /** - * Builds a CAR from a file map and returns the root CID + raw CAR bytes. - * Use this when you need both the CID and the bytes to upload to a pinning service. - */ -export async function buildCAR( - fileMap: Record, -): Promise<{ cid: string; carBuffer: Uint8Array }> { - const chunks: Uint8Array[] = [] - let rootCID: any - - const encoderStream = - Object.keys(fileMap).length === 1 - ? createFileEncoderStream(new Blob([Object.values(fileMap)[0]])) - : createDirectoryEncoderStream( - Object.entries(fileMap).map(([name, content]) => ({ - name, - stream: () => - new ReadableStream({ - start(controller) { - controller.enqueue(content) - controller.close() - }, - }), - })), - ) - - await encoderStream - .pipeThrough( - new TransformStream({ - transform(block, controller) { - rootCID = block.cid - controller.enqueue(block) - }, - }), - ) - .pipeThrough(new CAREncoderStream()) - .pipeTo( - new WritableStream({ - write(chunk) { - chunks.push(new Uint8Array(chunk)) - }, - }), - ) - - const total = chunks.reduce((sum, c) => sum + c.length, 0) - const carBuffer = new Uint8Array(total) - let offset = 0 - for (const chunk of chunks) { - carBuffer.set(chunk, offset) - offset += chunk.length - } - - return { cid: rootCID.toString(), carBuffer } -} - -/** - * Pre-computes the CID for a file or directory without retaining the CAR bytes. - * Used in the deposit flow where only the CID is needed. + * Pre-computes the IPFS-compatible CID for a file or directory. + * Always uses directory encoding so the CID matches what Pinata pins + * via fileArray — which is required for payment verification. */ export async function computeCID( fileMap: Record, ): Promise { try { - const { cid } = await buildCAR(fileMap) - return cid + const files = Object.entries(fileMap).map(([name, content]) => ({ + name, + stream: () => + new ReadableStream({ + start(controller) { + controller.enqueue(content) + controller.close() + }, + }), + })) + + let rootCID: any + let blockCount = 0 + + await createDirectoryEncoderStream(files) + .pipeThrough( + new TransformStream({ + transform(block, controller) { + blockCount++ + rootCID = block.cid + controller.enqueue(block) + }, + }), + ) + .pipeThrough(new CAREncoderStream()) + .pipeTo(new WritableStream()) + + logger.info('CID computed', { blockCount, cid: rootCID?.toString() }) + return rootCID.toString() } catch (error) { logger.error('Error computing CID', { error: error instanceof Error ? error.message : String(error), diff --git a/ui/package.json b/ui/package.json index 170da22..09420c2 100644 --- a/ui/package.json +++ b/ui/package.json @@ -28,8 +28,8 @@ "@tanstack/react-router": "^1.168.8", "@tanstack/react-router-devtools": "^1.166.11", "@tanstack/router-plugin": "^1.167.9", - "@toju.network/fil": "^0.2.2", - "@toju.network/sol": "^0.1.6", + "@toju.network/fil": "^1.0.0", + "@toju.network/sol": "^1.0.0", "dayjs": "^1.11.20", "embla-carousel-react": "^8.6.0", "framer-motion": "^11.18.2",