diff --git a/backend/.env.exemple b/backend/.env.exemple new file mode 100644 index 0000000..0b66c7d --- /dev/null +++ b/backend/.env.exemple @@ -0,0 +1,12 @@ +# Admin Wallet Configuration +# This wallet owns the Cap object and can add users to the whitelist +ADMIN_PRIVATE_KEY=your_private_key_here +ADMIN_ADDRESS=0x904f64f755764162a228a7da49b1288160597165ec60ebbf5fb9a94957db76c3 + +# Whitelist Contract IDs +PACKAGE_ID=0x0fe074f026b27ea8617d326dc732b635a762bb64e23b943bafc7ac49f8e9eb52 +WHITELIST_ID=0x7d4fdefe79f2b7332672e2289b331dcc445a47d2379a39bed95adbe91e5fcc7d +CAP_ID=0xcc9cb3bedca6f9e34f69420832b18cc24887dea30fe6616feec08113769051c5 + +# Sui Network +SUI_NETWORK=testnet diff --git a/backend/Dockerfile b/backend/Dockerfile index c68c818..79e2fe6 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -18,7 +18,7 @@ COPY . . RUN pnpm build # Expose the port -EXPOSE 3000 +EXPOSE 8000 # Start the application CMD ["pnpm", "start"] diff --git a/backend/docker-compose.dev.yml b/backend/docker-compose.dev.yml new file mode 100644 index 0000000..e4ccf43 --- /dev/null +++ b/backend/docker-compose.dev.yml @@ -0,0 +1,4 @@ +services: + backend: + ports: + - "8000:8000" diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml new file mode 100644 index 0000000..4c4e4f7 --- /dev/null +++ b/backend/docker-compose.yml @@ -0,0 +1,10 @@ +services: + backend: + build: + context: . + dockerfile: Dockerfile + env_file: + - .env + environment: + - PORT=8000 + restart: unless-stopped diff --git a/backend/move/Move.lock b/backend/move/Move.lock index bb5d0c2..3f2e499 100644 --- a/backend/move/Move.lock +++ b/backend/move/Move.lock @@ -21,7 +21,7 @@ dependencies = [ ] [move.toolchain-version] -compiler-version = "1.58.1" +compiler-version = "1.61.1" edition = "2024.beta" flavor = "sui" @@ -29,6 +29,6 @@ flavor = "sui" [env.testnet] chain-id = "4c78adac" -original-published-id = "0xc0ce2d9449d970ad1a5e85f6c0d5e214be416ba62a3fb8965a5a2f3259df73c6" -latest-published-id = "0xc0ce2d9449d970ad1a5e85f6c0d5e214be416ba62a3fb8965a5a2f3259df73c6" +original-published-id = "0xb30915eb3f8b706ca0d46981b5e23e74003f65bb521b641c3f53fe1a652062b7" +latest-published-id = "0xb30915eb3f8b706ca0d46981b5e23e74003f65bb521b641c3f53fe1a652062b7" published-version = "1" diff --git a/backend/move/sources/user_data.move b/backend/move/sources/user_data.move new file mode 100644 index 0000000..6c358b2 --- /dev/null +++ b/backend/move/sources/user_data.move @@ -0,0 +1,237 @@ +// Copyright (c), Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Module to store user data (strategies, execution history) on-chain +/// Replaces localStorage usage for decentralized data storage +module startHack::user_data { + use sui::table::{Self, Table}; + use std::string::String; + + // Error codes + const ENotOwner: u64 = 1; + const EStrategyNotFound: u64 = 2; + const EHistoryNotFound: u64 = 3; + + // Events + public struct StrategySaved has copy, drop { + user: address, + strategy_id: u64, + timestamp: u64, + } + + public struct StrategyDeleted has copy, drop { + user: address, + strategy_id: u64, + } + + public struct ExecutionRecorded has copy, drop { + user: address, + execution_id: u64, + timestamp: u64, + } + + public struct ExecutionHistoryCleared has copy, drop { + user: address, + } + + // Saved strategy data structure + public struct SavedStrategy has store, drop, copy { + id: u64, + name: String, + description: String, + strategy_json: String, // JSON stringified strategy + created_at: u64, + updated_at: u64, + } + + // Execution history entry + public struct ExecutionEntry has store, drop, copy { + id: u64, + workflow_name: String, + workflow_id: String, + status: String, // "success", "failed", "pending" + tx_digest: String, + timestamp: u64, + gas_used: u64, + result_data: String, // JSON stringified result + } + + // User data storage object + public struct UserDataStorage has key { + id: UID, + owner: address, + saved_strategies: Table, + strategy_counter: u64, + execution_history: Table, + execution_counter: u64, + } + + // Create a new user data storage + public entry fun create_storage(ctx: &mut TxContext) { + let storage = UserDataStorage { + id: object::new(ctx), + owner: ctx.sender(), + saved_strategies: table::new(ctx), + strategy_counter: 0, + execution_history: table::new(ctx), + execution_counter: 0, + }; + transfer::transfer(storage, ctx.sender()); + } + + // Save a new strategy + public entry fun save_strategy( + storage: &mut UserDataStorage, + name: String, + description: String, + strategy_json: String, + ctx: &mut TxContext + ) { + assert!(storage.owner == ctx.sender(), ENotOwner); + + let timestamp = ctx.epoch_timestamp_ms(); + let strategy_id = storage.strategy_counter; + + let strategy = SavedStrategy { + id: strategy_id, + name, + description, + strategy_json, + created_at: timestamp, + updated_at: timestamp, + }; + + storage.saved_strategies.add(strategy_id, strategy); + storage.strategy_counter = storage.strategy_counter + 1; + + sui::event::emit(StrategySaved { + user: ctx.sender(), + strategy_id, + timestamp, + }); + } + + // Update existing strategy + public entry fun update_strategy( + storage: &mut UserDataStorage, + strategy_id: u64, + name: String, + description: String, + strategy_json: String, + ctx: &mut TxContext + ) { + assert!(storage.owner == ctx.sender(), ENotOwner); + assert!(storage.saved_strategies.contains(strategy_id), EStrategyNotFound); + + let strategy = &mut storage.saved_strategies[strategy_id]; + strategy.name = name; + strategy.description = description; + strategy.strategy_json = strategy_json; + strategy.updated_at = ctx.epoch_timestamp_ms(); + } + + // Delete a strategy + public entry fun delete_strategy( + storage: &mut UserDataStorage, + strategy_id: u64, + ctx: &mut TxContext + ) { + assert!(storage.owner == ctx.sender(), ENotOwner); + assert!(storage.saved_strategies.contains(strategy_id), EStrategyNotFound); + + storage.saved_strategies.remove(strategy_id); + + sui::event::emit(StrategyDeleted { + user: ctx.sender(), + strategy_id, + }); + } + + // Record a workflow execution + public entry fun record_execution( + storage: &mut UserDataStorage, + workflow_name: String, + workflow_id: String, + status: String, + tx_digest: String, + gas_used: u64, + result_data: String, + ctx: &mut TxContext + ) { + assert!(storage.owner == ctx.sender(), ENotOwner); + + let timestamp = ctx.epoch_timestamp_ms(); + let execution_id = storage.execution_counter; + + let entry = ExecutionEntry { + id: execution_id, + workflow_name, + workflow_id, + status, + tx_digest, + timestamp, + gas_used, + result_data, + }; + + storage.execution_history.add(execution_id, entry); + storage.execution_counter = storage.execution_counter + 1; + + sui::event::emit(ExecutionRecorded { + user: ctx.sender(), + execution_id, + timestamp, + }); + } + + // Clear execution history + public entry fun clear_execution_history( + storage: &mut UserDataStorage, + ctx: &mut TxContext + ) { + assert!(storage.owner == ctx.sender(), ENotOwner); + + // Remove all entries from the table + let mut i = 0; + while (i < storage.execution_counter) { + if (storage.execution_history.contains(i)) { + storage.execution_history.remove(i); + }; + i = i + 1; + }; + storage.execution_counter = 0; + + sui::event::emit(ExecutionHistoryCleared { + user: ctx.sender(), + }); + } + + // View functions + public fun get_strategy_count(storage: &UserDataStorage): u64 { + storage.strategy_counter + } + + public fun get_execution_count(storage: &UserDataStorage): u64 { + storage.execution_counter + } + + public fun has_strategy(storage: &UserDataStorage, strategy_id: u64): bool { + storage.saved_strategies.contains(strategy_id) + } + + public fun has_execution(storage: &UserDataStorage, execution_id: u64): bool { + storage.execution_history.contains(execution_id) + } + + // Get strategy by ID (read-only access) + public fun get_strategy(storage: &UserDataStorage, strategy_id: u64): &SavedStrategy { + assert!(storage.saved_strategies.contains(strategy_id), EStrategyNotFound); + &storage.saved_strategies[strategy_id] + } + + // Get execution by ID (read-only access) + public fun get_execution(storage: &UserDataStorage, execution_id: u64): &ExecutionEntry { + assert!(storage.execution_history.contains(execution_id), EHistoryNotFound); + &storage.execution_history[execution_id] + } +} diff --git a/backend/package.json b/backend/package.json index 8dfd964..7a2599b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,12 +28,16 @@ "@cetusprotocol/cetus-sui-clmm-sdk": "^4.1.0", "@mysten/seal": "^0.9.4", "@mysten/sui": "^1.45.0", + "@types/swagger-jsdoc": "^6.0.4", + "@types/swagger-ui-express": "^4.1.8", "body-parser": "^2.2.0", "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", "multer": "^2.0.2", "navi-sdk": "^1.2.2", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1", "tsx": "^4.20.6", "uuid": "^11.0.3", "zod": "^3.23.8" @@ -42,9 +46,9 @@ "@types/body-parser": "^1.19.6", "@types/cors": "^2.8.19", "@types/express": "^5.0.5", + "@types/multer": "^1.4.11", "@types/node": "^22.10.1", "@types/uuid": "^10.0.0", - "@types/multer": "^1.4.11", "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", "eslint": "^9.16.0", diff --git a/backend/pnpm-lock.yaml b/backend/pnpm-lock.yaml index aa633cc..f525d35 100644 --- a/backend/pnpm-lock.yaml +++ b/backend/pnpm-lock.yaml @@ -17,6 +17,12 @@ importers: '@mysten/sui': specifier: ^1.45.0 version: 1.45.0(typescript@5.9.3) + '@types/swagger-jsdoc': + specifier: ^6.0.4 + version: 6.0.4 + '@types/swagger-ui-express': + specifier: ^4.1.8 + version: 4.1.8 body-parser: specifier: ^2.2.0 version: 2.2.0 @@ -35,6 +41,12 @@ importers: navi-sdk: specifier: ^1.2.2 version: 1.6.23(@types/node@22.19.1)(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10) + swagger-jsdoc: + specifier: ^6.2.8 + version: 6.2.8(openapi-types@12.1.3) + swagger-ui-express: + specifier: ^5.0.1 + version: 5.0.1(express@5.1.0) tsx: specifier: ^4.20.6 version: 4.20.6 @@ -98,6 +110,21 @@ packages: '@adraffy/ens-normalize@1.10.1': resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + '@apidevtools/json-schema-ref-parser@9.1.2': + resolution: {integrity: sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==} + + '@apidevtools/openapi-schemas@2.1.0': + resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} + engines: {node: '>=10'} + + '@apidevtools/swagger-methods@3.0.2': + resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} + + '@apidevtools/swagger-parser@10.0.3': + resolution: {integrity: sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==} + peerDependencies: + openapi-types: '>=7' + '@babel/runtime@7.28.4': resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} @@ -652,6 +679,9 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@mayanfinance/swap-sdk@10.3.0': resolution: {integrity: sha512-6wAfjRYQL9726DIUKcsT+hdUURI8QDJVXQ11yhX1IVknK0ypiFpPrFdAY2KcpifhVOE2osFUMZ+YqO95Y5LJ1A==} @@ -834,6 +864,9 @@ packages: cpu: [x64] os: [win32] + '@scarf/scarf@1.4.0': + resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + '@scure/base@1.2.6': resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} @@ -950,6 +983,12 @@ packages: '@types/serve-static@1.15.10': resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} + '@types/swagger-jsdoc@6.0.4': + resolution: {integrity: sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ==} + + '@types/swagger-ui-express@4.1.8': + resolution: {integrity: sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==} + '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} @@ -1184,6 +1223,9 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -1225,6 +1267,14 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@6.2.0: + resolution: {integrity: sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==} + engines: {node: '>= 6'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1307,6 +1357,10 @@ packages: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -1514,6 +1568,9 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1548,6 +1605,10 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + deprecated: Glob versions prior to v9 are no longer supported + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -1623,6 +1684,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -1717,9 +1782,20 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} @@ -1855,6 +1931,9 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1883,6 +1962,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2110,6 +2193,24 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + swagger-jsdoc@6.2.8: + resolution: {integrity: sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==} + engines: {node: '>=12.0.0'} + hasBin: true + + swagger-parser@10.0.3: + resolution: {integrity: sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==} + engines: {node: '>=10'} + + swagger-ui-dist@5.31.0: + resolution: {integrity: sha512-zSUTIck02fSga6rc0RZP3b7J7wgHXwLea8ZjgLA3Vgnb8QeOl3Wou2/j5QkzSGeoz6HusP/coYuJl33aQxQZpg==} + + swagger-ui-express@5.0.1: + resolution: {integrity: sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==} + engines: {node: '>= v0.10.32'} + peerDependencies: + express: '>=4.0.0 || >=5.0.0-beta' + text-encoding-utf-8@1.0.2: resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} @@ -2241,6 +2342,10 @@ packages: valibot@0.36.0: resolution: {integrity: sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==} + validator@13.15.23: + resolution: {integrity: sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==} + engines: {node: '>= 0.10'} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -2372,6 +2477,10 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + yaml@2.0.0-1: + resolution: {integrity: sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==} + engines: {node: '>= 6'} + yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -2384,6 +2493,11 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} + z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -2401,6 +2515,27 @@ snapshots: '@adraffy/ens-normalize@1.10.1': {} + '@apidevtools/json-schema-ref-parser@9.1.2': + dependencies: + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + call-me-maybe: 1.0.2 + js-yaml: 4.1.1 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@apidevtools/swagger-parser@10.0.3(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 9.1.2 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + z-schema: 5.0.5 + '@babel/runtime@7.28.4': {} '@cetusprotocol/cetus-sui-clmm-sdk@4.3.2(@mysten/bcs@1.9.2)(@mysten/sui.js@0.54.1(typescript@5.9.3))': @@ -2740,6 +2875,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@jsdevtools/ono@7.1.3': {} + '@mayanfinance/swap-sdk@10.3.0(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: '@mysten/sui': 1.45.0(typescript@5.9.3) @@ -2947,6 +3084,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.53.3': optional: true + '@scarf/scarf@1.4.0': {} + '@scure/base@1.2.6': {} '@scure/bip32@1.7.0': @@ -3096,6 +3235,13 @@ snapshots: '@types/node': 22.19.1 '@types/send': 0.17.6 + '@types/swagger-jsdoc@6.0.4': {} + + '@types/swagger-ui-express@4.1.8': + dependencies: + '@types/express': 5.0.5 + '@types/serve-static': 1.15.10 + '@types/uuid@10.0.0': {} '@types/uuid@8.3.4': {} @@ -3387,6 +3533,8 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + call-me-maybe@1.0.2: {} + callsites@3.1.0: {} chai@4.5.0: @@ -3436,6 +3584,11 @@ snapshots: commander@2.20.3: {} + commander@6.2.0: {} + + commander@9.5.0: + optional: true + concat-map@0.0.1: {} concat-stream@2.0.0: @@ -3498,6 +3651,10 @@ snapshots: diff@4.0.2: {} + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + dotenv@16.6.1: {} dotenv@17.2.3: {} @@ -3830,6 +3987,8 @@ snapshots: fresh@2.0.0: {} + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true @@ -3869,6 +4028,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@7.1.6: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + globals@14.0.0: {} gopd@1.2.0: {} @@ -3936,6 +4104,11 @@ snapshots: imurmurhash@0.1.4: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + inherits@2.0.4: {} ipaddr.js@1.9.1: {} @@ -4027,8 +4200,14 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.get@4.4.2: {} + + lodash.isequal@4.5.0: {} + lodash.merge@4.6.2: {} + lodash.mergewith@4.6.2: {} + loupe@2.3.7: dependencies: get-func-name: 2.0.2 @@ -4176,6 +4355,8 @@ snapshots: dependencies: mimic-fn: 4.0.0 + openapi-types@12.1.3: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -4205,6 +4386,8 @@ snapshots: path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-key@4.0.0: {} @@ -4465,6 +4648,32 @@ snapshots: dependencies: has-flag: 4.0.0 + swagger-jsdoc@6.2.8(openapi-types@12.1.3): + dependencies: + commander: 6.2.0 + doctrine: 3.0.0 + glob: 7.1.6 + lodash.mergewith: 4.6.2 + swagger-parser: 10.0.3(openapi-types@12.1.3) + yaml: 2.0.0-1 + transitivePeerDependencies: + - openapi-types + + swagger-parser@10.0.3(openapi-types@12.1.3): + dependencies: + '@apidevtools/swagger-parser': 10.0.3(openapi-types@12.1.3) + transitivePeerDependencies: + - openapi-types + + swagger-ui-dist@5.31.0: + dependencies: + '@scarf/scarf': 1.4.0 + + swagger-ui-express@5.0.1(express@5.1.0): + dependencies: + express: 5.1.0 + swagger-ui-dist: 5.31.0 + text-encoding-utf-8@1.0.2: {} tiny-invariant@1.3.3: {} @@ -4574,6 +4783,8 @@ snapshots: valibot@0.36.0: {} + validator@13.15.23: {} + vary@1.1.2: {} vite-node@1.6.1(@types/node@22.19.1): @@ -4676,10 +4887,20 @@ snapshots: xtend@4.0.2: {} + yaml@2.0.0-1: {} + yn@3.1.1: {} yocto-queue@0.1.0: {} yocto-queue@1.2.2: {} + z-schema@5.0.5: + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.15.23 + optionalDependencies: + commander: 9.5.0 + zod@3.25.76: {} diff --git a/backend/src/adapters/dex/cetus-adapter.ts b/backend/src/adapters/dex/cetus-adapter.ts index 3b1a3fa..3f2959f 100644 --- a/backend/src/adapters/dex/cetus-adapter.ts +++ b/backend/src/adapters/dex/cetus-adapter.ts @@ -9,20 +9,26 @@ import { Transaction } from "@mysten/sui/transactions"; import { CetusClmmSDK } from "@cetusprotocol/cetus-sui-clmm-sdk"; import { DexSwapNode, CetusSwapParams } from "../../types/strategy"; import { BaseDexAdapter, SwapEstimate } from "./types"; -import { MAINNET_ADDRESSES } from "../../config/addresses"; +import { getAddresses, Network } from "../../config/addresses"; export class CetusAdapter extends BaseDexAdapter { readonly protocol = "CETUS"; private sdk: CetusClmmSDK | null = null; + private network: Network; - constructor() { + constructor(network: Network = "mainnet") { super(); + this.network = network; - // Initialize Cetus SDK with required options (Mainnet) + // Initialize Cetus SDK with required options try { const config = this.getConfig(); + const rpcUrl = network === "mainnet" + ? "https://fullnode.mainnet.sui.io:443" + : "https://fullnode.testnet.sui.io:443"; + this.sdk = new CetusClmmSDK({ - fullRpcUrl: "https://fullnode.mainnet.sui.io:443", + fullRpcUrl: rpcUrl, simulationAccount: { address: "0x3c7ea737b5f0390399892c70e899498f819e7593eabad27466acfc59fedb979d" }, } as any); // Using any to bypass strict type check for now, as we only need RPC for pre-swap } catch (error) { @@ -31,7 +37,7 @@ export class CetusAdapter extends BaseDexAdapter { } private getConfig() { - return MAINNET_ADDRESSES; + return getAddresses(this.network); } async preSwap(node: DexSwapNode, estimatedInputAmount?: string): Promise { diff --git a/backend/src/adapters/dex/turbos-adapter.ts b/backend/src/adapters/dex/turbos-adapter.ts new file mode 100644 index 0000000..65c4473 --- /dev/null +++ b/backend/src/adapters/dex/turbos-adapter.ts @@ -0,0 +1,162 @@ +/** + * Turbos Finance DEX Adapter + * + * Integrates with Turbos CLMM (Concentrated Liquidity Market Maker). + * Turbos Finance is available on testnet and provides DEX swaps. + * + * Source: https://turbos.gitbook.io/turbos/developer-docs/dev-overview + */ + +import { Transaction } from "@mysten/sui/transactions"; +import { DexSwapNode, TurbosSwapParams } from "../../types/strategy"; +import { BaseDexAdapter, SwapEstimate } from "./types"; +import { getAddresses, Network, TESTNET_ADDRESSES } from "../../config/addresses"; + +export class TurbosAdapter extends BaseDexAdapter { + readonly protocol = "TURBOS"; + private network: Network; + + constructor(network: Network = "mainnet") { + super(); + this.network = network; + } + + private getConfig() { + return getAddresses(this.network); + } + + private getTurbosConfig() { + const config = this.getConfig(); + // Type guard: TURBOS only exists in TESTNET_ADDRESSES + if (this.network !== "testnet" || !("TURBOS" in config)) { + throw new Error("Turbos Finance is only available on testnet. Please use testnet network."); + } + return (config as typeof TESTNET_ADDRESSES).TURBOS; + } + + async preSwap(node: DexSwapNode, estimatedInputAmount?: string): Promise { + const params = node.params as TurbosSwapParams; + + // Get Turbos config (will throw if not on testnet) + const turbosConfig = this.getTurbosConfig(); + + // For Turbos, we need to simulate the swap to get estimates + // Since we don't have the SDK yet, we'll use a mock estimate + // TODO: Integrate Turbos SDK when available: npm install turbos-clmm-sdk + + const byAmountIn = params.amount_mode === "EXACT_IN"; + const mockAmount = params.amount === "ALL" + ? (estimatedInputAmount || "1000000") + : params.amount; + + // Mock estimate - in production, use Turbos SDK to get real estimates + return { + amount_in: byAmountIn ? mockAmount : "1000000", + amount_out: byAmountIn ? "1000000" : mockAmount, + price_impact: "0", + fee: "0", + // Turbos uses tick-based pricing, not sqrt_price_limit + sqrt_price_limit: undefined, + amount_limit: byAmountIn ? "0" : "18446744073709551615", + }; + } + + /** + * Add swap transaction to PTB using Turbos CLMM + * + * Turbos CLMM functions (based on standard CLMM pattern): + * - swap_exact_input_for_output: Swap exact input amount (A->B or B->A) + * - swap_exact_output_for_input: Swap exact output amount (A->B or B->A) + * + * Note: Turbos uses a2b parameter to determine swap direction + * - a2b = true: Swap from coin_type_a to coin_type_b + * - a2b = false: Swap from coin_type_b to coin_type_a + */ + swap(tx: Transaction, node: DexSwapNode, coinIn: any, estimate: SwapEstimate): any { + const params = node.params as TurbosSwapParams; + + // Get Turbos config (will throw if not on testnet) + const turbosConfig = this.getTurbosConfig(); + + const byAmountIn = params.amount_mode === "EXACT_IN"; + const user_a2b = params.direction === "A_TO_B"; + + // Determine which coin type is the input + const inputCoinType = user_a2b ? params.coin_type_a : params.coin_type_b; + const outputCoinType = user_a2b ? params.coin_type_b : params.coin_type_a; + + // Handle "ALL" amount + let amount: any; + if (params.amount === "ALL") { + amount = tx.moveCall({ + target: "0x2::coin::value", + arguments: [coinIn], + typeArguments: [inputCoinType], + }); + } else { + amount = tx.pure.u64(BigInt(params.amount)); + } + + // Calculate slippage-adjusted amount limit + const amountLimit = byAmountIn + ? this.calculateAmountLimit(estimate, params.slippage_tolerance, false) // min output + : this.calculateAmountLimit(estimate, params.slippage_tolerance, true); // max input + + // Turbos CLMM swap function signatures: + // swap_exact_input_for_output( + // pool_config: &PoolConfig, + // pool: &Pool, + // coin_a: Coin, + // amount: u64, + // min_amount_b: u64, + // a2b: bool, // Direction: true = A->B, false = B->A + // clock: &Clock, + // ctx: &mut TxContext + // ): Coin + // + // swap_exact_output_for_input( + // pool_config: &PoolConfig, + // pool: &Pool, + // coin_a: Coin, + // amount: u64, + // max_amount_a: u64, + // a2b: bool, // Direction: true = A->B, false = B->A + // clock: &Clock, + // ctx: &mut TxContext + // ): Coin + + if (byAmountIn) { + // swap_exact_input_for_output + const coinOut = tx.moveCall({ + target: `${turbosConfig.PACKAGE}::pool::swap_exact_input_for_output`, + arguments: [ + tx.object(turbosConfig.CONFIG.pool_config), + tx.object(params.pool_id), + coinIn, + amount, + tx.pure.u64(BigInt(amountLimit)), // min_amount_b (with slippage) + tx.pure.bool(user_a2b), // a2b direction + tx.object("0x6"), // Clock + ], + typeArguments: [params.coin_type_a, params.coin_type_b], + }); + return coinOut; + } else { + // swap_exact_output_for_input + const coinOut = tx.moveCall({ + target: `${turbosConfig.PACKAGE}::pool::swap_exact_output_for_input`, + arguments: [ + tx.object(turbosConfig.CONFIG.pool_config), + tx.object(params.pool_id), + coinIn, + amount, + tx.pure.u64(BigInt(amountLimit)), // max_amount_a (with slippage) + tx.pure.bool(user_a2b), // a2b direction + tx.object("0x6"), // Clock + ], + typeArguments: [params.coin_type_a, params.coin_type_b], + }); + return coinOut; + } + } +} diff --git a/backend/src/adapters/flashloan/navi-adapter.ts b/backend/src/adapters/flashloan/navi-adapter.ts index 540e7ba..cc27b0c 100644 --- a/backend/src/adapters/flashloan/navi-adapter.ts +++ b/backend/src/adapters/flashloan/navi-adapter.ts @@ -8,14 +8,14 @@ import { Transaction } from "@mysten/sui/transactions"; import { FlashBorrowNode, FlashRepayNode } from "../../types/strategy"; import { BaseFlashLoanAdapter, BorrowResult } from "./types"; -import { MAINNET_ADDRESSES } from "../../config/addresses"; +import { getAddresses } from "../../config/addresses"; export class NaviAdapter extends BaseFlashLoanAdapter { readonly protocol = "NAVI"; protected readonly feePercentage = 0.0006; // 0.06% private getConfig() { - return MAINNET_ADDRESSES; + return getAddresses(); } borrow(tx: Transaction, node: FlashBorrowNode): BorrowResult { diff --git a/backend/src/adapters/flashloan/turbos-adapter.ts b/backend/src/adapters/flashloan/turbos-adapter.ts new file mode 100644 index 0000000..22e4c04 --- /dev/null +++ b/backend/src/adapters/flashloan/turbos-adapter.ts @@ -0,0 +1,216 @@ +/** + * Turbos Finance Flash Loan Adapter + * + * Integrates with Turbos Protocol's flash swap functionality. + * Turbos uses "flash_swap" which is similar to flash loans. + * + * Source: https://turbos.gitbook.io/turbos/developer-docs/via-contract/contract-modules#flash-loan-functions + * + * Flash Swap Functions: + * - flash_swap: Executes flash swap, returns receipt that needs to be repaid + * - repay_flash_swap: Repays flash swap, returns remaining tokens + */ + +import { Transaction } from "@mysten/sui/transactions"; +import { FlashBorrowNode, FlashRepayNode } from "../../types/strategy"; +import { BaseFlashLoanAdapter, BorrowResult } from "./types"; +import { getAddresses, Network, TESTNET_ADDRESSES } from "../../config/addresses"; + +export class TurbosFlashLoanAdapter extends BaseFlashLoanAdapter { + readonly protocol = "TURBOS"; + protected readonly feePercentage = 0.0003; // 0.03% (Turbos flash swap fee, to be verified) + private network: Network; + + constructor(network: Network = "mainnet") { + super(); + this.network = network; + } + + private getConfig() { + return getAddresses(this.network); + } + + private getTurbosConfig() { + const config = this.getConfig(); + // Type guard: TURBOS only exists in TESTNET_ADDRESSES + if (this.network !== "testnet" || !("TURBOS" in config)) { + throw new Error("Turbos Finance is only available on testnet. Please use testnet network."); + } + return (config as typeof TESTNET_ADDRESSES).TURBOS; + } + + /** + * Borrow tokens using Turbos flash_swap + * + * Function signature: + * flash_swap( + * pool: &mut Pool, + * recipient: address, + * a_to_b: bool, + * amount_specified: u128, + * amount_specified_is_input: bool, + * sqrt_price_limit: u128, + * clock: &Clock, + * ctx: &mut TxContext + * ): FlashSwapReceipt + */ + borrow(tx: Transaction, node: FlashBorrowNode): BorrowResult { + const turbosConfig = this.getTurbosConfig(); + + // For Turbos flash swap, we need to determine: + // 1. Which pool to use (based on asset) + // 2. Direction (a_to_b) + // 3. Amount + + // Note: Turbos flash_swap requires a pool. We'll need the pool_id in params + // For now, we'll assume the asset is one side of a pool pair + // In production, you'd need to specify the pool_id and the other coin type + + const amount = BigInt(node.params.amount); + const amountU128 = amount; // Turbos uses u128 for amounts + + // Determine direction: if borrowing coin_type_a, a_to_b = false (we want to receive A) + // This needs to be determined based on the pool configuration + // For now, we'll assume a_to_b = false (borrowing token A) + const aToB = false; + + // sqrt_price_limit: 0 means no limit + const sqrtPriceLimit = BigInt(0); + + // amount_specified_is_input: true means we specify the input amount we want to receive + const amountSpecifiedIsInput = true; + + // Execute flash_swap + // Note: This requires pool_id and the other coin type to be specified + // We'll need to add these to FlashBorrowNode params for Turbos + // Recipient: Use provided recipient or default to sender (will be set at transaction execution) + const recipient = node.params.recipient || "0x0"; // Will be replaced by actual sender at execution + + if (!node.params.pool_id) { + throw new Error( + `❌ CONFIGURATION ERROR: Turbos flash_swap requires pool_id in FlashBorrowParams.\n\n` + + `Your strategy is missing the required "pool_id" parameter for Turbos Finance.\n\n` + + `💡 Solution: Add "pool_id" to your strategy params:\n` + + ` "params": {\n` + + ` "asset": "0x2::sui::SUI",\n` + + ` "amount": "1000000000",\n` + + ` "pool_id": "0x...", // ← Add Turbos pool ID here\n` + + ` "coin_type_a": "0x2::sui::SUI",\n` + + ` "coin_type_b": "0x...::usdc::USDC"\n` + + ` }\n\n` + + `📚 Find Turbos pools on testnet: https://suiscan.xyz/testnet\n` + + `This is NOT a balance issue - your wallet balance is sufficient.` + ); + } + + if (!node.params.coin_type_a || !node.params.coin_type_b) { + throw new Error( + `❌ CONFIGURATION ERROR: Turbos flash_swap requires coin_type_a and coin_type_b in FlashBorrowParams.\n\n` + + `Your strategy is missing the required coin type parameters for Turbos Finance.\n\n` + + `💡 Solution: Add "coin_type_a" and "coin_type_b" to your strategy params:\n` + + ` "params": {\n` + + ` "asset": "0x2::sui::SUI",\n` + + ` "amount": "1000000000",\n` + + ` "pool_id": "0x...",\n` + + ` "coin_type_a": "0x2::sui::SUI", // ← Add first coin type\n` + + ` "coin_type_b": "0x...::usdc::USDC" // ← Add second coin type\n` + + ` }\n\n` + + `📚 Find Turbos pools on testnet: https://suiscan.xyz/testnet\n` + + `This is NOT a balance issue - your wallet balance is sufficient.` + ); + } + + const receipt = tx.moveCall({ + target: `${turbosConfig.PACKAGE}::pool::flash_swap`, + arguments: [ + tx.object(node.params.pool_id), + tx.pure.address(recipient), // Recipient (will be sender if not specified) + tx.pure.bool(aToB), // a_to_b direction + tx.pure.u128(amountU128), // amount_specified + tx.pure.bool(amountSpecifiedIsInput), // amount_specified_is_input + tx.pure.u128(sqrtPriceLimit), // sqrt_price_limit + tx.object("0x6"), // Clock + ], + typeArguments: [ + node.params.coin_type_a, + node.params.coin_type_b, + "0x0", // FeeType - needs to be specified in params + ], + }); + + // Flash swap returns a receipt, not a coin directly + // We need to extract the coin from the receipt or use a different approach + // For now, we'll return the receipt and handle coin extraction in repay + + // Note: Turbos flash_swap might work differently - need to check the actual return type + // The receipt contains the borrowed tokens that need to be repaid + + return { + coin: receipt, // This might need adjustment based on actual Turbos implementation + receipt: receipt + }; + } + + /** + * Repay flash swap + * + * Function signature: + * repay_flash_swap( + * pool: &mut Pool, + * coin_a: Coin, + * coin_b: Coin, + * receipt: FlashSwapReceipt, + * ctx: &mut TxContext + * ): (Coin, Coin) + */ + repay(tx: Transaction, node: FlashRepayNode, coin: any, receipt: any, borrowedAmount?: bigint): void { + const turbosConfig = this.getTurbosConfig(); + + if (!borrowedAmount) { + throw new Error("Turbos flash_swap repayment requires borrowedAmount to be provided"); + } + + if (!node.params.pool_id) { + throw new Error("Turbos flash_swap repayment requires pool_id in FlashRepayParams"); + } + + if (!node.params.coin_type_a || !node.params.coin_type_b) { + throw new Error("Turbos flash_swap repayment requires coin_type_a and coin_type_b in FlashRepayParams"); + } + + // Calculate repayment amount (borrowed + fee) + const repayAmount = this.getRepayAmount(borrowedAmount); + + // For Turbos, we need to repay with both coins potentially + // The receipt tells us what needs to be repaid + // We'll need coin_a and coin_b + + // Split the coin to get the exact repayment amount + const repayCoin = tx.moveCall({ + target: "0x2::coin::split", + arguments: [ + coin, + tx.pure.u64(repayAmount), + ], + typeArguments: [node.params.asset], + }); + + // Repay flash swap + // Note: This might need both coin_a and coin_b depending on the swap direction + // For now, we'll assume we only need one coin + tx.moveCall({ + target: `${turbosConfig.PACKAGE}::pool::repay_flash_swap`, + arguments: [ + tx.object(node.params.pool_id || ""), // Pool ID + repayCoin, // coin_a + tx.object("0x2::coin::Coin"), // coin_b - might need to be empty or zero + receipt, // FlashSwapReceipt + ], + typeArguments: [ + node.params.coin_type_a || node.params.asset, + node.params.coin_type_b || "", + "0x0", // FeeType + ], + }); + } +} diff --git a/backend/src/adapters/perp/aftermath-perp-adapter.ts b/backend/src/adapters/perp/aftermath-perp-adapter.ts new file mode 100644 index 0000000..862ed51 --- /dev/null +++ b/backend/src/adapters/perp/aftermath-perp-adapter.ts @@ -0,0 +1,101 @@ +/** + * Aftermath Finance Perpetual DEX Adapter + * + * NOTE: Aftermath Perpetuals is currently only available on Mainnet. + * This adapter is prepared for when testnet support is added. + * + * For now, this serves as a template for implementing perp DEX adapters. + */ + +import { Transaction } from "@mysten/sui/transactions"; +import { PerpOpenNode, PerpCloseNode } from "../../types/strategy"; +import { BasePerpAdapter, PerpEstimate } from "./types"; +import { getAddresses } from "../../config/addresses"; + +export class AftermathPerpAdapter extends BasePerpAdapter { + readonly protocol = "AFTERMATH_PERP"; + + private getConfig() { + return getAddresses(); + } + + async preOpenPosition(node: PerpOpenNode): Promise { + const params = node.params; + const config = this.getConfig(); + + // TODO: Implement actual pre-open simulation when Aftermath Perp is available on testnet + // For now, return mock estimate + const leverage = params.leverage || "10"; + const positionSize = params.size; + const collateralRequired = this.calculateRequiredCollateral(positionSize, leverage); + + return { + position_size: positionSize, + collateral_required: collateralRequired, + leverage: leverage, + liquidation_price: "0", // TODO: Calculate from market price + funding_rate: "0.0001", // 0.01% per hour (example) + fee: "0", // TODO: Calculate opening fee + }; + } + + openPosition(tx: Transaction, node: PerpOpenNode, collateral: any, estimate: PerpEstimate): any { + const params = node.params; + const config = this.getConfig(); + + // TODO: Implement actual open position call when Aftermath Perp is available on testnet + // Example structure: + // const [position] = tx.moveCall({ + // target: `${config.PERP_DEX.PACKAGE}::perp::open_position`, + // arguments: [ + // tx.object(config.PERP_DEX.CONFIG.clearing_house), + // tx.object(params.market_id), + // collateral, + // tx.pure.u64(BigInt(params.size)), + // tx.pure.bool(params.direction === "LONG"), + // tx.pure.u8(parseInt(params.leverage || "10")), + // ], + // typeArguments: [params.collateral_type], + // }); + // return position; + + // Placeholder: return collateral for now (will fail validation, but structure is ready) + console.warn("Aftermath Perp is not yet available on testnet. This is a placeholder implementation."); + return collateral; + } + + async preClosePosition(node: PerpCloseNode): Promise { + const params = node.params; + + // TODO: Implement actual pre-close simulation + return { + position_size: params.size || "0", + collateral_required: "0", + leverage: "0", + liquidation_price: "0", + fee: "0", // TODO: Calculate closing fee + }; + } + + closePosition(tx: Transaction, node: PerpCloseNode, position: any, estimate: PerpEstimate): any { + const params = node.params; + const config = this.getConfig(); + + // TODO: Implement actual close position call when Aftermath Perp is available on testnet + // Example structure: + // const [collateral] = tx.moveCall({ + // target: `${config.PERP_DEX.PACKAGE}::perp::close_position`, + // arguments: [ + // tx.object(config.PERP_DEX.CONFIG.clearing_house), + // position, + // params.size ? tx.pure.u64(BigInt(params.size)) : undefined, + // ], + // typeArguments: [], + // }); + // return collateral; + + // Placeholder: return position for now + console.warn("Aftermath Perp is not yet available on testnet. This is a placeholder implementation."); + return position; + } +} diff --git a/backend/src/adapters/perp/types.ts b/backend/src/adapters/perp/types.ts new file mode 100644 index 0000000..134a7d0 --- /dev/null +++ b/backend/src/adapters/perp/types.ts @@ -0,0 +1,104 @@ +/** + * Perp DEX Adapter Interface + * + * Standardized interface for all Perpetual DEX protocols. + * Each protocol (Aftermath, HyperSui, Astros, PerpSea) implements this interface. + */ + +import { Transaction } from "@mysten/sui/transactions"; +import { PerpOpenNode, PerpCloseNode } from "../../types/strategy"; + +/** + * Position estimate result + */ +export interface PerpEstimate { + position_size: string; // Actual position size + collateral_required: string; // Collateral needed + leverage: string; // Actual leverage + liquidation_price: string; // Price at which position would be liquidated + funding_rate?: string; // Current funding rate + fee: string; // Opening/closing fee +} + +/** + * Standard interface all Perp DEX adapters must implement + */ +export interface PerpAdapter { + /** + * Protocol name + */ + readonly protocol: string; + + /** + * Simulate opening a position to get estimates + * MUST be called before openPosition() for accurate calculations + * + * @param node - Perp open node from strategy + * @returns Position estimate + */ + preOpenPosition(node: PerpOpenNode): Promise; + + /** + * Add open position transaction to PTB + * + * @param tx - Transaction block + * @param node - Perp open node from strategy + * @param collateral - Input collateral coin + * @param estimate - Pre-open estimate (from preOpenPosition) + * @returns Position ID or receipt + */ + openPosition(tx: Transaction, node: PerpOpenNode, collateral: any, estimate: PerpEstimate): any; + + /** + * Simulate closing a position to get estimates + * + * @param node - Perp close node from strategy + * @returns Close estimate (collateral returned, PnL, etc.) + */ + preClosePosition(node: PerpCloseNode): Promise; + + /** + * Add close position transaction to PTB + * + * @param tx - Transaction block + * @param node - Perp close node from strategy + * @param position - Position ID or receipt from open + * @param estimate - Pre-close estimate (from preClosePosition) + * @returns Collateral returned (with PnL) + */ + closePosition(tx: Transaction, node: PerpCloseNode, position: any, estimate: PerpEstimate): any; +} + +/** + * Base adapter class with common functionality + */ +export abstract class BasePerpAdapter implements PerpAdapter { + abstract readonly protocol: string; + + abstract preOpenPosition(node: PerpOpenNode): Promise; + abstract openPosition(tx: Transaction, node: PerpOpenNode, collateral: any, estimate: PerpEstimate): any; + abstract preClosePosition(node: PerpCloseNode): Promise; + abstract closePosition(tx: Transaction, node: PerpCloseNode, position: any, estimate: PerpEstimate): any; + + /** + * Calculate liquidation price + * Formula: liquidation_price = entry_price * (1 - 1/leverage) + */ + protected calculateLiquidationPrice(entryPrice: string, leverage: string): string { + const entry = parseFloat(entryPrice); + const lev = parseFloat(leverage); + const liquidation = entry * (1 - 1 / lev); + return liquidation.toString(); + } + + /** + * Calculate required collateral + * Formula: collateral = position_size / leverage + */ + protected calculateRequiredCollateral(positionSize: string, leverage: string): string { + const size = BigInt(positionSize); + const lev = BigInt(parseFloat(leverage) * 1000000); // Use fixed point for precision + const collateral = (size * BigInt(1000000)) / lev; + return collateral.toString(); + } +} diff --git a/backend/src/api/routes/build.ts b/backend/src/api/routes/build.ts index ba58806..c7a592c 100644 --- a/backend/src/api/routes/build.ts +++ b/backend/src/api/routes/build.ts @@ -20,21 +20,24 @@ router.post('/build', async (req: Request, res: Response): Promise => { return; } - // Build the transaction - const builder = new TransactionBuilder(); + // Get network from query param or env, default to mainnet + const network = (req.query.network as string) || process.env.SUI_NETWORK || 'mainnet'; + + // Build the transaction with network + const builder = new TransactionBuilder(network as any); const tx = await builder.buildFromStrategy(strategy as Strategy); // Set the sender tx.setSender(sender); // Initialize client - const client = new SuiClient({ url: getFullnodeUrl('mainnet') }); + const client = new SuiClient({ url: getFullnodeUrl(network as any) }); // Get reference gas price const rgp = await client.getReferenceGasPrice(); - console.log('Fetched RGP from Mainnet:', rgp); + console.log(`Fetched RGP from ${network}:`, rgp); - // Force gas price to be at least 1000 MIST (Mainnet minimum) to avoid 505 error + // Force gas price to be at least 1000 MIST (Testnet minimum) to avoid 505 error const gasPrice = rgp > 1000n ? rgp : 1000n; tx.setGasPrice(gasPrice); console.log('Setting transaction gas price to:', gasPrice); diff --git a/backend/src/api/routes/simulation.ts b/backend/src/api/routes/simulation.ts index d6a2a51..e67d54d 100644 --- a/backend/src/api/routes/simulation.ts +++ b/backend/src/api/routes/simulation.ts @@ -5,8 +5,9 @@ import { Strategy } from '../../types/strategy'; const router: Router = Router(); -// Initialize simulator with Mainnet -const simulator = new Simulator('mainnet'); +// Initialize simulator with Mainnet (default) +// Can be overridden per request via query param +const defaultSimulator = new Simulator('mainnet'); router.post('/simulate', async (req: Request, res: Response): Promise => { try { @@ -20,6 +21,10 @@ router.post('/simulate', async (req: Request, res: Response): Promise => { return; } + // Get network from query param or env, default to mainnet + const network = (req.query.network as string) || process.env.SUI_NETWORK || 'mainnet'; + const simulator = network === 'testnet' ? new Simulator('testnet') : defaultSimulator; + // Run simulation const result = await simulator.simulate(strategy as Strategy, sender); diff --git a/backend/src/api/routes/swagger-annotations.ts b/backend/src/api/routes/swagger-annotations.ts new file mode 100644 index 0000000..4c6f050 --- /dev/null +++ b/backend/src/api/routes/swagger-annotations.ts @@ -0,0 +1,489 @@ +/** + * Ce fichier contient toutes les annotations Swagger pour les routes de l'API + * Les annotations sont centralisées ici pour faciliter la maintenance + */ + +/** + * @swagger + * /api/tokens: + * get: + * tags: [Tokens] + * summary: Get supported tokens + * description: Returns a list of all supported tokens from NAVI protocol + * responses: + * 200: + * description: List of supported tokens + * content: + * application/json: + * schema: + * type: object + * additionalProperties: + * type: string + * example: + * SUI: "0x..." + * USDC: "0x..." + */ + +/** + * @swagger + * /api/simulate: + * post: + * tags: [Simulation] + * summary: Simulate a DeFi strategy + * description: Simulates the execution of a strategy and returns gas estimates and profit/loss + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - strategy + * - sender + * properties: + * strategy: + * $ref: '#/components/schemas/Strategy' + * sender: + * type: string + * description: Sender wallet address + * responses: + * 200: + * description: Simulation results + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * estimated_gas: + * type: number + * estimated_profit_loss: + * type: array + * items: + * type: object + * errors: + * type: array + * items: + * type: object + * 400: + * description: Missing required fields + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + */ + +/** + * @swagger + * /api/validate: + * post: + * tags: [Validation] + * summary: Validate a strategy + * description: Validates a strategy structure and returns any errors or warnings + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - strategy + * properties: + * strategy: + * $ref: '#/components/schemas/Strategy' + * responses: + * 200: + * description: Validation results + * content: + * application/json: + * schema: + * type: object + * properties: + * valid: + * type: boolean + * errors: + * type: array + * items: + * type: object + * warnings: + * type: array + * items: + * type: object + */ + +/** + * @swagger + * /api/build: + * post: + * tags: [Build] + * summary: Build a transaction from a strategy + * description: Builds a Sui transaction from a strategy that can be signed and executed + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - strategy + * - sender + * properties: + * strategy: + * $ref: '#/components/schemas/Strategy' + * sender: + * type: string + * description: Sender wallet address + * responses: + * 200: + * description: Transaction bytes ready to be signed + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * transactionBytes: + * type: string + * description: Base64 encoded transaction bytes + */ + +/** + * @swagger + * /api/workflows/upload: + * post: + * tags: [Workflows] + * summary: Upload a workflow to the marketplace + * description: Encrypts and stores a workflow on Walrus, then adds it to the marketplace + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Strategy' + * responses: + * 200: + * description: Workflow uploaded successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * workflowId: + * type: string + * metadataBlobId: + * type: string + */ + +/** + * @swagger + * /api/workflows/list: + * get: + * tags: [Workflows] + * summary: List all marketplace workflows + * description: Returns all workflows available in the marketplace + * responses: + * 200: + * description: List of workflows + * content: + * application/json: + * schema: + * type: object + * properties: + * workflows: + * type: array + * items: + * type: object + */ + +/** + * @swagger + * /api/seal/encrypt: + * post: + * tags: [Seal] + * summary: Encrypt and store a file on Walrus + * description: Encrypts a file and stores it on Walrus using the Seal protocol + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * required: + * - file + * properties: + * file: + * type: string + * format: binary + * responses: + * 200: + * description: File encrypted and stored + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * metadataBlobId: + * type: string + * dataBlobId: + * type: string + * nonce: + * type: string + * originalSize: + * type: number + */ + +/** + * @swagger + * /api/userdata/create-storage: + * post: + * tags: [User Data] + * summary: Create user data storage + * description: Creates a new UserDataStorage object on-chain for the user + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - userAddress + * properties: + * userAddress: + * type: string + * description: User wallet address + * responses: + * 200: + * description: Transaction bytes to create storage + * content: + * application/json: + * schema: + * type: object + * properties: + * transactionBytes: + * type: array + * items: + * type: number + */ + +/** + * @swagger + * /api/userdata/storage/{address}: + * get: + * tags: [User Data] + * summary: Get user storage object ID + * description: Returns the storage object ID for a user address + * parameters: + * - in: path + * name: address + * required: true + * schema: + * type: string + * description: User wallet address + * responses: + * 200: + * description: Storage object information + * content: + * application/json: + * schema: + * type: object + * properties: + * storageObjectId: + * type: string + * nullable: true + * exists: + * type: boolean + */ + +/** + * @swagger + * /api/userdata/save-strategy: + * post: + * tags: [User Data] + * summary: Save a strategy on-chain + * description: Builds a transaction to save a strategy to the user's on-chain storage + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - userAddress + * - storageObjectId + * - name + * - strategyJson + * properties: + * userAddress: + * type: string + * storageObjectId: + * type: string + * name: + * type: string + * description: + * type: string + * strategyJson: + * type: object + * responses: + * 200: + * description: Transaction bytes to save strategy + */ + +/** + * @swagger + * /api/userdata/strategies/{storageObjectId}: + * get: + * tags: [User Data] + * summary: Get user strategies + * description: Returns all strategies saved by the user + * parameters: + * - in: path + * name: storageObjectId + * required: true + * schema: + * type: string + * responses: + * 200: + * description: List of user strategies + * content: + * application/json: + * schema: + * type: object + * properties: + * strategies: + * type: array + * items: + * $ref: '#/components/schemas/SavedStrategy' + */ + +/** + * @swagger + * /api/userdata/delete-strategy: + * post: + * tags: [User Data] + * summary: Delete a strategy + * description: Builds a transaction to delete a strategy from on-chain storage + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - userAddress + * - storageObjectId + * - strategyId + * properties: + * userAddress: + * type: string + * storageObjectId: + * type: string + * strategyId: + * type: number + * responses: + * 200: + * description: Transaction bytes to delete strategy + */ + +/** + * @swagger + * /api/userdata/record-execution: + * post: + * tags: [User Data] + * summary: Record workflow execution + * description: Builds a transaction to record a workflow execution on-chain + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - userAddress + * - storageObjectId + * - workflowName + * - status + * - txDigest + * properties: + * userAddress: + * type: string + * storageObjectId: + * type: string + * workflowName: + * type: string + * workflowId: + * type: string + * status: + * type: string + * enum: [success, failed, pending] + * txDigest: + * type: string + * gasUsed: + * type: number + * resultData: + * type: object + * responses: + * 200: + * description: Transaction bytes to record execution + */ + +/** + * @swagger + * /api/userdata/history/{storageObjectId}: + * get: + * tags: [User Data] + * summary: Get execution history + * description: Returns all workflow executions recorded for the user + * parameters: + * - in: path + * name: storageObjectId + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Execution history + * content: + * application/json: + * schema: + * type: object + * properties: + * history: + * type: array + * items: + * $ref: '#/components/schemas/ExecutionEntry' + */ + +/** + * @swagger + * /api/userdata/clear-history: + * post: + * tags: [User Data] + * summary: Clear execution history + * description: Builds a transaction to clear all execution history + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - userAddress + * - storageObjectId + * properties: + * userAddress: + * type: string + * storageObjectId: + * type: string + * responses: + * 200: + * description: Transaction bytes to clear history + */ diff --git a/backend/src/api/routes/userdata.ts b/backend/src/api/routes/userdata.ts new file mode 100644 index 0000000..c9efca7 --- /dev/null +++ b/backend/src/api/routes/userdata.ts @@ -0,0 +1,258 @@ +import express, { Request, Response, Router } from 'express'; +import { UserDataService } from '../../services/UserDataService'; +import { getSuiClient } from '../../services/SuiClientService'; + +const router: Router = express.Router(); +const suiClient = getSuiClient(); +const userDataService = new UserDataService(suiClient); + +/** + * POST /api/userdata/create-storage + * Build transaction to create UserDataStorage object + */ +router.post('/create-storage', async (req: Request, res: Response) => { + try { + const { userAddress } = req.body; + + if (!userAddress) { + return res.status(400).json({ error: 'userAddress is required' }); + } + + const txBytes = await userDataService.buildCreateStorageTransaction(userAddress); + const serializedTx = Array.from(txBytes); + + res.json({ transactionBytes: serializedTx }); + } catch (error: any) { + console.error('Error building create storage transaction:', error); + res.status(500).json({ error: error.message || 'Failed to build transaction' }); + } +}); + +/** + * GET /api/userdata/storage/:address + * Get user's storage object ID + */ +router.get('/storage/:address', async (req: Request, res: Response) => { + try { + const { address } = req.params; + const storageObjectId = await userDataService.getUserStorageObjectId(address); + + if (!storageObjectId) { + return res.json({ storageObjectId: null, exists: false }); + } + + res.json({ storageObjectId, exists: true }); + } catch (error: any) { + console.error('Error getting storage object:', error); + res.status(500).json({ error: error.message || 'Failed to get storage object' }); + } +}); + +/** + * POST /api/userdata/save-strategy + * Build transaction to save a new strategy + */ +router.post('/save-strategy', async (req: Request, res: Response) => { + try { + const { userAddress, storageObjectId, name, description, strategyJson } = req.body; + + if (!userAddress || !storageObjectId || !name || !strategyJson) { + return res.status(400).json({ + error: 'userAddress, storageObjectId, name, and strategyJson are required', + }); + } + + const txBytes = await userDataService.buildSaveStrategyTransaction( + userAddress, + storageObjectId, + name, + description || '', + JSON.stringify(strategyJson) + ); + + const serializedTx = Array.from(txBytes); + res.json({ transactionBytes: serializedTx }); + } catch (error: any) { + console.error('Error building save strategy transaction:', error); + res.status(500).json({ error: error.message || 'Failed to build transaction' }); + } +}); + +/** + * POST /api/userdata/update-strategy + * Build transaction to update existing strategy + */ +router.post('/update-strategy', async (req: Request, res: Response) => { + try { + const { userAddress, storageObjectId, strategyId, name, description, strategyJson } = req.body; + + if (!userAddress || !storageObjectId || strategyId === undefined || !name || !strategyJson) { + return res.status(400).json({ + error: 'userAddress, storageObjectId, strategyId, name, and strategyJson are required', + }); + } + + const txBytes = await userDataService.buildUpdateStrategyTransaction( + userAddress, + storageObjectId, + strategyId, + name, + description || '', + JSON.stringify(strategyJson) + ); + + const serializedTx = Array.from(txBytes); + res.json({ transactionBytes: serializedTx }); + } catch (error: any) { + console.error('Error building update strategy transaction:', error); + res.status(500).json({ error: error.message || 'Failed to build transaction' }); + } +}); + +/** + * POST /api/userdata/delete-strategy + * Build transaction to delete a strategy + */ +router.post('/delete-strategy', async (req: Request, res: Response) => { + try { + const { userAddress, storageObjectId, strategyId } = req.body; + + if (!userAddress || !storageObjectId || strategyId === undefined) { + return res.status(400).json({ + error: 'userAddress, storageObjectId, and strategyId are required', + }); + } + + const txBytes = await userDataService.buildDeleteStrategyTransaction( + userAddress, + storageObjectId, + strategyId + ); + + const serializedTx = Array.from(txBytes); + res.json({ transactionBytes: serializedTx }); + } catch (error: any) { + console.error('Error building delete strategy transaction:', error); + res.status(500).json({ error: error.message || 'Failed to build transaction' }); + } +}); + +/** + * POST /api/userdata/record-execution + * Build transaction to record a workflow execution + */ +router.post('/record-execution', async (req: Request, res: Response) => { + try { + const { + userAddress, + storageObjectId, + workflowName, + workflowId, + status, + txDigest, + gasUsed, + resultData, + } = req.body; + + if (!userAddress || !storageObjectId || !workflowName || !status || !txDigest) { + return res.status(400).json({ + error: 'userAddress, storageObjectId, workflowName, status, and txDigest are required', + }); + } + + const txBytes = await userDataService.buildRecordExecutionTransaction( + userAddress, + storageObjectId, + workflowName, + workflowId || '', + status, + txDigest, + gasUsed || 0, + JSON.stringify(resultData || {}) + ); + + const serializedTx = Array.from(txBytes); + res.json({ transactionBytes: serializedTx }); + } catch (error: any) { + console.error('Error building record execution transaction:', error); + res.status(500).json({ error: error.message || 'Failed to build transaction' }); + } +}); + +/** + * POST /api/userdata/clear-history + * Build transaction to clear execution history + */ +router.post('/clear-history', async (req: Request, res: Response) => { + try { + const { userAddress, storageObjectId } = req.body; + + if (!userAddress || !storageObjectId) { + return res.status(400).json({ + error: 'userAddress and storageObjectId are required', + }); + } + + const txBytes = await userDataService.buildClearHistoryTransaction(userAddress, storageObjectId); + + const serializedTx = Array.from(txBytes); + res.json({ transactionBytes: serializedTx }); + } catch (error: any) { + console.error('Error building clear history transaction:', error); + res.status(500).json({ error: error.message || 'Failed to build transaction' }); + } +}); + +/** + * GET /api/userdata/strategies/:storageObjectId + * Get all saved strategies for a user + */ +router.get('/strategies/:storageObjectId', async (req: Request, res: Response) => { + try { + const { storageObjectId } = req.params; + const strategies = await userDataService.getUserStrategies(storageObjectId); + + res.json({ strategies }); + } catch (error: any) { + console.error('Error getting strategies:', error); + res.status(500).json({ error: error.message || 'Failed to get strategies' }); + } +}); + +/** + * GET /api/userdata/strategy/:storageObjectId/:strategyId + * Get a specific strategy by ID + */ +router.get('/strategy/:storageObjectId/:strategyId', async (req: Request, res: Response) => { + try { + const { storageObjectId, strategyId } = req.params; + const strategy = await userDataService.getStrategy(storageObjectId, parseInt(strategyId)); + + if (!strategy) { + return res.status(404).json({ error: 'Strategy not found' }); + } + + res.json({ strategy }); + } catch (error: any) { + console.error('Error getting strategy:', error); + res.status(500).json({ error: error.message || 'Failed to get strategy' }); + } +}); + +/** + * GET /api/userdata/history/:storageObjectId + * Get execution history for a user + */ +router.get('/history/:storageObjectId', async (req: Request, res: Response) => { + try { + const { storageObjectId } = req.params; + const history = await userDataService.getUserExecutionHistory(storageObjectId); + + res.json({ history }); + } catch (error: any) { + console.error('Error getting execution history:', error); + res.status(500).json({ error: error.message || 'Failed to get execution history' }); + } +}); + +export default router; diff --git a/backend/src/api/server.ts b/backend/src/api/server.ts index 69ee71f..414392f 100644 --- a/backend/src/api/server.ts +++ b/backend/src/api/server.ts @@ -2,11 +2,14 @@ import express from 'express'; import cors from 'cors'; import bodyParser from 'body-parser'; +import swaggerUi from 'swagger-ui-express'; +import { swaggerSpec } from '../config/swagger'; import simulationRoutes from './routes/simulation'; import validateRoutes from './routes/validate'; import sealRoutes from './routes/seal'; import workflowsRoutes from './routes/workflows'; import buildRoutes from './routes/build'; +import userdataRoutes from './routes/userdata'; const app = express(); const port = process.env.PORT || 8000; @@ -15,26 +18,78 @@ const port = process.env.PORT || 8000; app.use(cors()); app.use(bodyParser.json()); +// Swagger Documentation +app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, { + customCss: '.swagger-ui .topbar { display: none }', + customSiteTitle: 'SAIL API Documentation', +})); + +// Swagger JSON +app.get('/docs.json', (req, res) => { + res.setHeader('Content-Type', 'application/json'); + res.send(swaggerSpec); +}); + // Routes app.use('/api', simulationRoutes); app.use('/api', validateRoutes); app.use('/api', sealRoutes); app.use('/api', workflowsRoutes); app.use('/api', buildRoutes); +app.use('/api/userdata', userdataRoutes); -// Health Check +/** + * @swagger + * /api/health: + * get: + * tags: [Health] + * summary: Health check endpoint + * description: Returns the health status of the API + * responses: + * 200: + * description: API is healthy + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: ok + */ app.get('/api/health', (req, res) => { res.json({ status: 'ok' }); }); // Get Supported Tokens (Testnet for now) -import { TESTNET_ADDRESSES, PoolConfig } from '../config/addresses'; +import { getAddresses, PoolConfig } from '../config/addresses'; +/** + * @swagger + * /api/tokens: + * get: + * tags: [Tokens] + * summary: Get supported tokens + * description: Returns a list of all supported tokens from NAVI protocol + * responses: + * 200: + * description: List of supported tokens + * content: + * application/json: + * schema: + * type: object + * additionalProperties: + * type: string + * example: + * SUI: "0x..." + * USDC: "0x..." + */ app.get('/api/tokens', (req, res) => { const tokens: Record = {}; - + // Extract tokens from NAVI Pools config - Object.entries(TESTNET_ADDRESSES.NAVI.POOLS).forEach(([address, pool]) => { + const addresses = getAddresses(); + Object.entries(addresses.NAVI.POOLS).forEach(([address, pool]) => { const poolConfig = pool as PoolConfig; tokens[poolConfig.name] = address; }); diff --git a/backend/src/config/addresses.ts b/backend/src/config/addresses.ts index 2e7c1af..c2a142f 100644 --- a/backend/src/config/addresses.ts +++ b/backend/src/config/addresses.ts @@ -1,3 +1,25 @@ +/** + * Network Configuration Helper + * + * Use getAddresses() function to get addresses for the current network + * Defaults to mainnet if SUI_NETWORK env var is not set + */ + +export type Network = "mainnet" | "testnet" | "devnet"; + +/** + * Get addresses for the specified network + * Defaults to mainnet if network is not provided + */ +export function getAddresses(network?: Network): typeof MAINNET_ADDRESSES | typeof TESTNET_ADDRESSES { + const envNetwork = process.env.SUI_NETWORK as Network; + const targetNetwork = network || envNetwork || "mainnet"; + + if (targetNetwork === "mainnet") { + return MAINNET_ADDRESSES; + } + return TESTNET_ADDRESSES; +} /** * Navi Protocol Mainnet Addresses @@ -69,9 +91,83 @@ export const MAINNET_ADDRESSES = { global_config_id: "0xdaa46292632c3c4d8f31f23ea0f9b36a28ff3677e9684980e4438403a67a3d8f", // CLMM Global Config global_vault_id: "0xce7bceef26d3ad1f6d9b6f13a953f053e6ed3ca77907516481ce99ae8e588f2b" } - } + }, + // Turbos Finance is only available on testnet, not mainnet + // TURBOS: undefined on mainnet }; -// For now, use MAINNET_ADDRESSES as TESTNET_ADDRESSES -// TODO: Add proper testnet addresses when available -export const TESTNET_ADDRESSES = MAINNET_ADDRESSES; +/** + * Testnet Addresses + * + * Note: Navi and Cetus testnet addresses need to be verified on SuiScan + * Turbos Finance testnet addresses are from official documentation + */ +export const TESTNET_ADDRESSES = { + NAVI: { + // ⚠️ TODO: Verify Navi testnet addresses on SuiScan + // For now, using mainnet addresses (will fail on testnet) + PACKAGE: "0xee0041239b89564ce870a7dec5ddc5d114367ab94a1137e90aa0633cb76518e0", + STORAGE: "0xbb4e2f4b6205c2e2a2db47aeb4f830796ec7c005f88537ee775986639bc442fe", + PRICE_ORACLE: "0x1568865ed9a0b5ec414220e8f79b3d04c77acc82358f6e5ae4635687392ffbef", + FLASHLOAN_CONFIG: "0x3672b2bf471a60c30a03325f104f92fb195c9d337ba58072dce764fe2aa5e2dc", + INCENTIVE_V2: "0xf87a8acb8b81d14307894d12595541a73f19933f88e1326d5be349c7a6f7559c", + INCENTIVE_V3: "0x62982dad27fb10bb314b3384d5de8d2ac2d72ab2dbeae5d801dbdb9efa816c80", + POOLS: { + "0x2::sui::SUI": { + poolId: "0x96df0fce3c471489f4debaaa762cf960b3d97820bd1f3f025ff8190730e958c5", + assetId: 0, + name: "SUI" + }, + "0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN": { // wUSDC + poolId: "0xa02a98f9c88db51c6f5efaaf2261c81f34dd56d86073387e0ef1805ca22e39c8", + assetId: 1, + name: "wUSDC" + }, + "0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN": { // USDT + poolId: "0x0e060c3b5b8de00fb50511b7a45188c8e34b6995c01f69d98ea5a466fe10d103", + assetId: 2, + name: "USDT" + } + } + }, + CETUS: { + // ⚠️ TODO: Verify Cetus testnet addresses on SuiScan + // For now, using mainnet addresses (will fail on testnet) + PACKAGE: "0xb2db7142fa83210a7d78d9c12ac49c043b3cbbd482224fea6e3da00aa5a5ae2d", + CONFIG: { + pools_id: "0x1eabed72c53feb3805120a081dc15963c204dc8d091542592abaf7a35689b2fb", + global_config_id: "0xdaa46292632c3c4d8f31f23ea0f9b36a28ff3677e9684980e4438403a67a3d8f", + global_vault_id: "0xce7bceef26d3ad1f6d9b6f13a953f053e6ed3ca77907516481ce99ae8e588f2b" + } + }, + TURBOS: { + /** + * Turbos Finance Testnet Addresses + * + * Source: https://turbos.gitbook.io/turbos/developer-docs/dev-overview#testnet-1 + * Verified: December 2025 + * + * Turbos Finance is a Concentrated Liquidity Market Maker (CLMM) DEX + * Available on testnet for swaps and liquidity provision + */ + PACKAGE: "0x3526c88f5304c78fb93ed1cc1961d56b8517108550c9938b8a5a0e6c90fbe2a5", // ✅ CLMM Package Testnet + PUBLISHED_AT: "0xbce833e2aa9266eacad68c8db25e83cb52941a2eba168c4a28bb4aa38a2be9ea", + CONFIG: { + pool_config: "0x2041acff5d98b1892ac5be1faa075936a05f64922657dc94c374c43ce2291a8d", // ✅ Pool Config Testnet + positions: "0x8d916e3eaa3a5ce2949a4d845ec8082f7d46768ffd4c15984c32b3c5f4cabf22", // ✅ Positions Testnet + versioned: "0x0ec5aedfc4a3a99aebd8a54b6b39df34b7696ada57008c35f69d6b4bb346b5c4", // ✅ Versioned Testnet + }, + FEE_TIERS: { + // Fee tiers are shared objects for different fee levels + "10bps": "0x082ef6ca8112c65a3ceff44c7dd160221ef92d18ad6c0314c95a443dffe16df3", + "100bps": "0x26b21a736613daa65462d13f3e79b2fe09072a596ec5b0466313df08ab217577", + "500bps": "0x15d883f76337c7062817eb2231dc62e291474cac4147ee47047029d7df5c7ffb", + "1000bps": "0x82e9c6626655965f2b5a0b33428a193b7b367d258f0e6b9a43f66efa20ff4f29", + "2000bps": "0x55799e99561b546d84b194fd81d87aa40b1f91a86e84afb9b51b7efbc6ea2be0", + "2500bps": "0x73c168d6ec583288ddff9e9cd180ed8c6f0f94f5a77b37b7716bae5f62245bd1", + "3000bps": "0xdafb286b57abcbb4bd5be7f7930587048250002d4b1f60cdf0874026a3cb4709", + "10000bps": "0xf7791adbed4be7a4276b1d8d5f83279a709663acc51753fe6bbcf6eb9e2a878c", + "20000bps": "0x4bf5fb4974bce137ae1c4b6fb6a4149eb14c7654f268be0d2b4a4a4efecd432e" + } + } +}; diff --git a/backend/src/config/admin.ts b/backend/src/config/admin.ts index 67ba480..3777708 100644 --- a/backend/src/config/admin.ts +++ b/backend/src/config/admin.ts @@ -10,6 +10,20 @@ export const ADMIN_CONFIG = { CAP_ID: process.env.CAP_ID || '', }; +// Export individual values for convenience +export const PACKAGE_ID = process.env.PACKAGE_ID || ''; +export const WHITELIST_ID = process.env.WHITELIST_ID || ''; +export const CAP_ID = process.env.CAP_ID || ''; + +/** + * Get admin signer (keypair) from environment variable + * If not set, returns null (admin functions will be disabled) + * @deprecated Use getAdminKeypair instead + */ +export function getAdminSigner(): Ed25519Keypair | null { + return getAdminKeypair(); +} + /** * Get admin keypair from environment variable * If not set, returns null (admin functions will be disabled) diff --git a/backend/src/config/swagger.ts b/backend/src/config/swagger.ts new file mode 100644 index 0000000..2665568 --- /dev/null +++ b/backend/src/config/swagger.ts @@ -0,0 +1,127 @@ +import swaggerJsdoc from 'swagger-jsdoc'; + +const options: swaggerJsdoc.Options = { + definition: { + openapi: '3.0.0', + info: { + title: 'SAIL - Sui AI Liquidity API', + version: '1.0.0', + description: 'API pour la plateforme SAIL - DeFi workflow builder avec encryption Walrus et gestion des données utilisateur on-chain', + contact: { + name: 'SAIL Team', + }, + }, + servers: [ + { + url: 'http://localhost:8000', + description: 'Development server', + }, + ], + tags: [ + { + name: 'Health', + description: 'Health check endpoints', + }, + { + name: 'Simulation', + description: 'Workflow simulation endpoints', + }, + { + name: 'Validation', + description: 'Strategy validation endpoints', + }, + { + name: 'Build', + description: 'Transaction building endpoints', + }, + { + name: 'Workflows', + description: 'Marketplace workflow management (upload, list, purchase)', + }, + { + name: 'Seal', + description: 'File encryption/decryption with Walrus storage', + }, + { + name: 'User Data', + description: 'On-chain user data management (strategies, execution history)', + }, + { + name: 'Tokens', + description: 'Supported tokens information', + }, + ], + components: { + schemas: { + Strategy: { + type: 'object', + properties: { + id: { type: 'string', description: 'Strategy unique identifier' }, + meta: { + type: 'object', + properties: { + name: { type: 'string' }, + description: { type: 'string' }, + }, + }, + nodes: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'string' }, + type: { type: 'string', enum: ['defi', 'condition', 'loop'] }, + data: { type: 'object' }, + }, + }, + }, + edges: { + type: 'array', + items: { + type: 'object', + properties: { + source: { type: 'string' }, + target: { type: 'string' }, + }, + }, + }, + }, + }, + SavedStrategy: { + type: 'object', + properties: { + id: { type: 'number' }, + name: { type: 'string' }, + description: { type: 'string' }, + strategyJson: { type: 'object' }, + createdAt: { type: 'number' }, + updatedAt: { type: 'number' }, + }, + }, + ExecutionEntry: { + type: 'object', + properties: { + id: { type: 'number' }, + workflowName: { type: 'string' }, + workflowId: { type: 'string' }, + status: { type: 'string', enum: ['success', 'failed', 'pending'] }, + txDigest: { type: 'string' }, + timestamp: { type: 'number' }, + gasUsed: { type: 'number' }, + resultData: { type: 'object' }, + }, + }, + Error: { + type: 'object', + properties: { + error: { type: 'string' }, + message: { type: 'string' }, + }, + }, + }, + }, + }, + apis: ['./src/api/routes/swagger-annotations.ts', './src/api/server.ts'], +}; + +export const swaggerSpec = swaggerJsdoc(options); diff --git a/backend/src/core/simulator.ts b/backend/src/core/simulator.ts index 55d9241..7339efa 100644 --- a/backend/src/core/simulator.ts +++ b/backend/src/core/simulator.ts @@ -21,7 +21,7 @@ export class Simulator { private client: SuiClient; constructor(private readonly network: "mainnet" | "testnet" = "mainnet") { - this.builder = new TransactionBuilder(); + this.builder = new TransactionBuilder(this.network); this.client = new SuiClient({ url: this.getFullNodeUrl() }); } @@ -36,6 +36,11 @@ export class Simulator { // Reset builder state this.builder.reset(); + // Log simulation start with network info + console.log(`[SIMULATOR] Starting simulation on ${this.network}`); + console.log(`[SIMULATOR] Sender: ${sender}`); + console.log(`[SIMULATOR] Strategy ID: ${strategy.id}`); + const result: SimulationResult = { success: false, estimated_gas: 0, @@ -48,8 +53,10 @@ export class Simulator { try { // 1. Build the transaction // This also performs validation and pre-simulation (swap estimates) + console.log(`[SIMULATOR] Building transaction with network: ${this.network}`); const tx = await this.builder.buildFromStrategy(strategy); tx.setSender(sender); + console.log(`[SIMULATOR] Transaction built successfully`); // Capture swap estimates from builder if available // Note: TransactionBuilder doesn't expose cache publicly yet. @@ -111,19 +118,39 @@ export class Simulator { // Transaction failed result.success = false; const errorMsg = dryRunResult.effects.status.error || "Unknown error"; - console.error("Dry run failed with error:", errorMsg); - // For debugging, return raw error + + // Log full error details for debugging + console.error("=== DRY RUN FAILED ==="); + console.error("Network:", this.network); + console.error("Error from effects:", errorMsg); + console.error("Full effects:", JSON.stringify(dryRunResult.effects, null, 2)); + console.error("====================="); + + // Parse error message with improved error detection + const userMessage = this.parseExecutionError(errorMsg); + result.errors.push({ rule_id: "dry_run_failed", severity: "ERROR", - message: errorMsg, + message: userMessage, }); } } catch (error: any) { result.success = false; - console.error("Simulation error details:", error); - const userMessage = this.parseExecutionError(error.message || "An unexpected error occurred during simulation"); + + // Log full error details for debugging + console.error("=== SIMULATION ERROR DETAILS ==="); + console.error("Network:", this.network); + console.error("Error message:", error.message); + console.error("Error stack:", error.stack); + console.error("Full error object:", JSON.stringify(error, null, 2)); + console.error("================================"); + + // Parse error message - prioritize package/object errors over balance errors + const errorMessage = error.message || error.toString() || "An unexpected error occurred during simulation"; + const userMessage = this.parseExecutionError(errorMessage); + result.errors.push({ rule_id: "simulation_error", severity: "ERROR", @@ -136,8 +163,83 @@ export class Simulator { /** * Parse Move execution errors and return user-friendly messages + * Distinguishes clearly between: + * 1. Package/Object does not exist (configuration issue) + * 2. Insufficient balance (funding issue) */ private parseExecutionError(errorMsg: string): string { + // =================================================================== + // PRIORITY 1: Package/Object does not exist (CONFIGURATION ERROR) + // =================================================================== + + // Handle "Package object does not exist" - This is a CONFIGURATION issue, not a balance issue + if (errorMsg.includes("Package object does not exist") || errorMsg.includes("does not exist with ID")) { + // Extract package ID if present + const packageIdMatch = errorMsg.match(/0x[a-fA-F0-9]{64}/); + const packageId = packageIdMatch ? packageIdMatch[0] : null; + + let message = `❌ CONFIGURATION ERROR: Package does not exist on ${this.network}.\n\n`; + + if (packageId) { + message += `Package ID: ${packageId}\n\n`; + + // Check if it's a known package and suggest the issue + if (packageId === "0x8200ce83e1bc0894b641f0a466694b4f6e25d3f9cc3093915a887ec9e7f3395e") { + message += `⚠️ This is the old Navi Protocol package ID.\n`; + message += `The package may not exist on ${this.network}, or you need to use the updated package ID.\n\n`; + message += `Action: Check the correct package ID for ${this.network} on SuiScan:\n`; + message += `https://suiscan.xyz/${this.network}\n`; + } else if (packageId === "0xee0041239b89564ce870a7dec5ddc5d114367ab94a1137e90aa0633cb76518e0") { + if (this.network === "testnet") { + message += `⚠️ This is the Navi Protocol mainnet package ID (upgrade Nov 2025).\n`; + message += `Navi Protocol is not available on testnet.\n\n`; + message += `💡 On testnet, use Turbos Finance for flash loans and swaps:\n`; + message += ` - Flash Loans: Use protocol "TURBOS" with Turbos flash_swap\n`; + message += ` - DEX Swaps: Use protocol "TURBOS" with Turbos pools\n`; + message += ` - Turbos Package: 0x3526c88f5304c78fb93ed1cc1961d56b8517108550c9938b8a5a0e6c90fbe2a5\n\n`; + message += `Action: Switch your strategy to use "TURBOS" protocol on testnet, or switch to mainnet for Navi.\n`; + } else { + message += `⚠️ This is the Navi Protocol mainnet package ID (upgrade Nov 2025).\n`; + message += `Action: Verify this package exists on ${this.network}:\n`; + message += `https://suiscan.xyz/${this.network}/package/${packageId}\n`; + } + } else { + message += `Action: Verify this package exists on ${this.network}:\n`; + message += `https://suiscan.xyz/${this.network}/package/${packageId}\n`; + } + } else { + message += `Action: Check your strategy configuration and verify all package IDs are correct for ${this.network}.\n`; + } + + message += `\nThis is NOT a balance issue - your wallet balance is sufficient.`; + return message; + } + + // Handle "Object does not exist" - Similar configuration issue + if (errorMsg.includes("Object does not exist") || errorMsg.includes("Object ID") && errorMsg.includes("does not exist")) { + const objectIdMatch = errorMsg.match(/0x[a-fA-F0-9]{64}/); + const objectId = objectIdMatch ? objectIdMatch[0] : null; + + let message = `❌ CONFIGURATION ERROR: Object does not exist on ${this.network}.\n\n`; + + if (objectId) { + message += `Object ID: ${objectId}\n\n`; + message += `This could be:\n`; + message += `- STORAGE, FLASHLOAN_CONFIG, or other protocol objects\n`; + message += `- A pool ID that doesn't exist\n`; + message += `- An incorrect object ID in your configuration\n\n`; + message += `Action: Verify the object exists on ${this.network}:\n`; + message += `https://suiscan.xyz/${this.network}/object/${objectId}\n`; + } + + message += `\nThis is NOT a balance issue - your wallet balance is sufficient.`; + return message; + } + + // =================================================================== + // PRIORITY 2: Insufficient Balance (FUNDING ERROR) + // =================================================================== + // Handle "could not automatically determine a budget" errors with MoveAbort if (errorMsg.includes("could not automatically determine a budget") && errorMsg.includes("MoveAbort")) { // Extract abort code @@ -145,7 +247,10 @@ export class Simulator { const abortCode = abortCodeMatch ? parseInt(abortCodeMatch[1]) : null; if (abortCode === 1503) { - return "Insufficient balance: You don't have enough SUI on mainnet to cover the transaction. Make sure you have sufficient balance to cover the borrowed amount + gas fees."; + return `💰 INSUFFICIENT BALANCE: You don't have enough SUI on ${this.network}.\n\n` + + `Required: Borrowed amount + flash loan fees (0.06%) + gas fees\n\n` + + `Action: Add more SUI to your wallet on ${this.network}.\n` + + `This is a FUNDING issue, not a configuration issue.`; } if (abortCode === 1502) { return "Flash loan repayment error: The repayment amount is incorrect. The borrowed amount plus fees must be repaid exactly."; @@ -154,7 +259,10 @@ export class Simulator { return "Assertion failed in flash loan contract. Check your strategy logic."; } - return `Budget error: The protocol cannot determine the required budget (error code: ${abortCode || "unknown"}). You probably don't have enough SUI on mainnet.`; + return `💰 INSUFFICIENT BALANCE: The protocol cannot determine the required budget (error code: ${abortCode || "unknown"}).\n\n` + + `This usually means you don't have enough SUI on ${this.network}.\n\n` + + `Action: Add more SUI to your wallet.\n` + + `This is a FUNDING issue, not a configuration issue.`; } // Handle Move abort errors (e.g., "MoveAbort(MoveLocation { ... }, 1503)") @@ -165,7 +273,10 @@ export class Simulator { // Map common abort codes to user-friendly messages if (abortCode === 1503) { - return "Insufficient balance: You don't have enough SUI on mainnet to cover the transaction. Make sure you have sufficient balance to cover the borrowed amount + gas fees."; + return `💰 INSUFFICIENT BALANCE: You don't have enough SUI on ${this.network}.\n\n` + + `Required: Borrowed amount + flash loan fees (0.06%) + gas fees\n\n` + + `Action: Add more SUI to your wallet on ${this.network}.\n` + + `This is a FUNDING issue, not a configuration issue.`; } if (abortCode === 1502) { return "Flash loan repayment error: The repayment amount is incorrect. The borrowed amount plus fees must be repaid exactly."; @@ -178,7 +289,12 @@ export class Simulator { } // Generic message for other abort codes - return `Execution error (Error code: ${abortCode || "unknown"}). You may not have enough funds or there may be an issue with your strategy logic on mainnet.`; + return `⚠️ EXECUTION ERROR (Error code: ${abortCode || "unknown"}).\n\n` + + `Possible causes:\n` + + `- Insufficient balance (add more SUI)\n` + + `- Strategy logic issue\n` + + `- Configuration issue (wrong package/object IDs)\n\n` + + `Check your wallet balance and strategy configuration on ${this.network}.`; } // Handle unused value errors (bytecode verification) @@ -186,25 +302,62 @@ export class Simulator { return "Unused value error: A coin or object was created but not used. In Sui, you cannot simply drop coins with value. You must merge them, transfer them, or destroy them (if zero)."; } - // Handle other error patterns + // Handle other error patterns related to balance if (errorMsg.includes("could not automatically determine a budget")) { - return "Unable to determine gas budget: You may not have enough SUI on mainnet for gas fees. Make sure you have sufficient balance."; + return `💰 INSUFFICIENT BALANCE: Unable to determine gas budget.\n\n` + + `You may not have enough SUI on ${this.network} for gas fees.\n\n` + + `Action: Add more SUI to your wallet.\n` + + `This is a FUNDING issue, not a configuration issue.`; } - if (errorMsg.includes("balance")) { - return "Insufficient balance on mainnet: You don't have enough SUI to execute this strategy. Please add more funds to your wallet."; + if (errorMsg.includes("balance") && (errorMsg.includes("insufficient") || errorMsg.includes("not enough"))) { + return `💰 INSUFFICIENT BALANCE on ${this.network}.\n\n` + + `You don't have enough SUI to execute this strategy.\n\n` + + `Action: Add more SUI to your wallet.\n` + + `This is a FUNDING issue, not a configuration issue.`; } - if (errorMsg.includes("gas")) { - return "Insufficient gas on mainnet: You don't have enough SUI to pay for gas fees. Please add more funds to your wallet."; + if (errorMsg.includes("gas") && (errorMsg.includes("insufficient") || errorMsg.includes("not enough"))) { + return `💰 INSUFFICIENT GAS on ${this.network}.\n\n` + + `You don't have enough SUI to pay for gas fees.\n\n` + + `Action: Add more SUI to your wallet.\n` + + `This is a FUNDING issue, not a configuration issue.`; } - if (errorMsg.includes("coin")) { - return "Coin error on mainnet: There may be an issue with the coins in your strategy. Check that all coin types are correct."; + if (errorMsg.includes("coin") && !errorMsg.includes("does not exist")) { + return `⚠️ COIN ERROR on ${this.network}.\n\n` + + `There may be an issue with the coins in your strategy.\n\n` + + `Action: Check that all coin types are correct.\n` + + `This could be a CONFIGURATION issue (wrong coin types) or a FUNDING issue (insufficient coins).`; } - // Return original error with a generic note - return `Execution error on mainnet: ${errorMsg}. Make sure you have sufficient SUI balance and check your strategy.`; + // =================================================================== + // GENERIC ERROR - Try to determine if it's configuration or funding + // =================================================================== + + // Check for common configuration error patterns + if (errorMsg.includes("does not exist") || + errorMsg.includes("not found") || + errorMsg.includes("invalid") && (errorMsg.includes("package") || errorMsg.includes("object"))) { + return `❌ CONFIGURATION ERROR on ${this.network}.\n\n` + + `Error: ${errorMsg}\n\n` + + `This is likely a configuration issue:\n` + + `- Wrong package ID for ${this.network}\n` + + `- Wrong object ID (STORAGE, FLASHLOAN_CONFIG, etc.)\n` + + `- Package/object doesn't exist on ${this.network}\n\n` + + `Action: Verify all package and object IDs are correct for ${this.network}.\n` + + `Check: https://suiscan.xyz/${this.network}\n\n` + + `This is NOT a balance issue.`; + } + + // Default: Return original error with clear categorization attempt + return `⚠️ EXECUTION ERROR on ${this.network}.\n\n` + + `Error: ${errorMsg}\n\n` + + `Possible causes:\n` + + `1. ❌ CONFIGURATION: Wrong package/object IDs for ${this.network}\n` + + `2. 💰 FUNDING: Insufficient SUI balance\n` + + `3. ⚠️ LOGIC: Strategy logic issue\n\n` + + `Check your configuration and wallet balance.`; } private getFullNodeUrl(): string { diff --git a/backend/src/core/transaction-builder.ts b/backend/src/core/transaction-builder.ts index e777fe1..9c5dd37 100644 --- a/backend/src/core/transaction-builder.ts +++ b/backend/src/core/transaction-builder.ts @@ -28,8 +28,10 @@ import { GraphValidator } from "../validation/graph-validator"; import { TopologicalSort } from "../utils/topological-sort"; import { FlashLoanAdapter } from "../adapters/flashloan/types"; import { NaviAdapter } from "../adapters/flashloan/navi-adapter"; +import { TurbosFlashLoanAdapter } from "../adapters/flashloan/turbos-adapter"; import { DexAdapter } from "../adapters/dex/types"; import { CetusAdapter } from "../adapters/dex/cetus-adapter"; +import { TurbosAdapter } from "../adapters/dex/turbos-adapter"; /** * Result cache entry @@ -52,27 +54,32 @@ export class TransactionBuilder { private flashLoanAdapters: Map; private dexAdapters: Map; - constructor() { + private network: "mainnet" | "testnet" | "devnet"; + + constructor(network: "mainnet" | "testnet" | "devnet" = "mainnet") { this.tx = new Transaction(); this.resultCache = new Map(); this.swapEstimateCache = new Map(); - - // Initialize adapters (Mainnet only) - this.flashLoanAdapters = new Map([ - ["NAVI", new NaviAdapter()], - // Add more flash loan adapters here - // ["DEEPBOOK_V3", new DeepBookV3Adapter()], - // ["SCALLOP", new ScallopAdapter()], - // ["BUCKET", new BucketAdapter()], - ]); - - this.dexAdapters = new Map([ - ["CETUS", new CetusAdapter()], - // Add more DEX adapters here - // ["DEEPBOOK_V3", new DeepBookV3DexAdapter()], - // ["TURBOS", new TurbosAdapter()], - // ["AFTERMATH_ROUTER", new AftermathAdapter()], - ]); + this.network = network; + + // Initialize adapters with network configuration + this.flashLoanAdapters = new Map(); + this.flashLoanAdapters.set("NAVI", new NaviAdapter()); + if (network === "testnet") { + // Turbos is only available on testnet + this.flashLoanAdapters.set("TURBOS", new TurbosFlashLoanAdapter(network)); + } + // Add more flash loan adapters here + // this.flashLoanAdapters.set("DEEPBOOK_V3", new DeepBookV3Adapter()); + // this.flashLoanAdapters.set("SCALLOP", new ScallopAdapter()); + // this.flashLoanAdapters.set("BUCKET", new BucketAdapter()); + + this.dexAdapters = new Map(); + this.dexAdapters.set("CETUS", new CetusAdapter(network)); + this.dexAdapters.set("TURBOS", new TurbosAdapter(network)); + // Add more DEX adapters here + // this.dexAdapters.set("DEEPBOOK_V3", new DeepBookV3DexAdapter()); + // this.dexAdapters.set("AFTERMATH_ROUTER", new AftermathAdapter()); } /** @@ -174,13 +181,88 @@ export class TransactionBuilder { // ========================================================================== /** - * Add flash borrow node + * Protocol Router: Automatically redirect protocols based on network + * - On testnet: NAVI → TURBOS (Navi not available on testnet) + * - On mainnet: Keep original protocol + * + * Also adapts node parameters when routing (e.g., Navi params → Turbos params) */ - private addFlashBorrow(node: FlashBorrowNode): void { - const adapter = this.flashLoanAdapters.get(node.protocol); + private getFlashLoanAdapter(protocol: string, node?: FlashBorrowNode | FlashRepayNode): FlashLoanAdapter { + let targetProtocol = protocol; + let needsParamAdaptation = false; + + // Auto-redirect: Navi → Turbos on testnet + if (this.network === "testnet" && protocol === "NAVI") { + targetProtocol = "TURBOS"; + needsParamAdaptation = true; + console.log(`🔄 Auto-routing: NAVI → TURBOS on testnet (Navi not available on testnet)`); + + // Validate that required Turbos params are present + if (node && node.type === "FLASH_BORROW") { + const params = (node as FlashBorrowNode).params; + if (!params.pool_id || !params.coin_type_a || !params.coin_type_b) { + throw new Error( + `❌ CONFIGURATION ERROR: Your strategy uses protocol "NAVI" which is not available on testnet.\n\n` + + `The system automatically routes to "TURBOS" on testnet, but Turbos requires additional parameters:\n` + + ` - pool_id: The Turbos pool ID for the token pair\n` + + ` - coin_type_a: First coin type in the pool (e.g., "0x2::sui::SUI")\n` + + ` - coin_type_b: Second coin type in the pool\n\n` + + `💡 Solution: Update your strategy to include these parameters:\n` + + ` {\n` + + ` "protocol": "TURBOS",\n` + + ` "params": {\n` + + ` "asset": "0x2::sui::SUI",\n` + + ` "amount": "1000000000",\n` + + ` "pool_id": "0x...", // ← Add this\n` + + ` "coin_type_a": "0x2::sui::SUI", // ← Add this\n` + + ` "coin_type_b": "0x...::usdc::USDC" // ← Add this\n` + + ` }\n` + + ` }\n\n` + + `📚 Find Turbos pools on testnet: https://suiscan.xyz/testnet\n` + + `This is NOT a balance issue - your wallet balance is sufficient.` + ); + } + } + } + + const adapter = this.flashLoanAdapters.get(targetProtocol); + if (!adapter) { + if (this.network === "testnet" && protocol === "NAVI") { + throw new Error( + `Protocol "NAVI" is not available on testnet. ` + + `Turbos Finance (TURBOS) is available on testnet for flash loans and swaps. ` + + `Please update your strategy to use protocol "TURBOS" instead of "NAVI" when on testnet.` + ); + } + throw new Error(`No adapter found for flash loan protocol: ${protocol} on ${this.network}`); + } + + return adapter; + } + + /** + * Protocol Router for DEX: Automatically redirect protocols based on network + * - On testnet: Can redirect if needed (currently Cetus and Turbos both available) + */ + private getDexAdapter(protocol: string): DexAdapter { + let targetProtocol = protocol; + + // Auto-redirect logic for DEX if needed in the future + // For now, both Cetus and Turbos are available on testnet + + const adapter = this.dexAdapters.get(targetProtocol); if (!adapter) { - throw new Error(`No adapter found for flash loan protocol: ${node.protocol}`); + throw new Error(`No adapter found for DEX protocol: ${protocol} on ${this.network}`); } + + return adapter; + } + + /** + * Add flash borrow node + */ + private addFlashBorrow(node: FlashBorrowNode): void { + const adapter = this.getFlashLoanAdapter(node.protocol, node); const { coin, receipt } = adapter.borrow(this.tx, node); @@ -200,21 +282,21 @@ export class TransactionBuilder { * Add flash repay node */ private addFlashRepay(node: FlashRepayNode, strategy: Strategy): void { - const adapter = this.flashLoanAdapters.get(node.protocol); - if (!adapter) { - throw new Error(`No adapter found for flash loan protocol: ${node.protocol}`); - } - - // Resolve inputs - const coin = this.resolveReference(node.inputs.coin_repay); - const receipt = this.resolveReference(node.inputs.receipt); - - // Find the corresponding borrow node to get the amount + // Find the corresponding borrow node to get the amount and determine the actual protocol used // The receipt input is in format "nodeId.outputId" const receiptRef = node.inputs.receipt; const [borrowNodeId] = receiptRef.split("."); const borrowNode = strategy.nodes.find((n) => n.id === borrowNodeId) as FlashBorrowNode; + // Use the same protocol routing as the borrow node + // If borrow was NAVI on testnet, it was routed to TURBOS, so repay should also use TURBOS + const actualProtocol = borrowNode ? borrowNode.protocol : node.protocol; + const adapter = this.getFlashLoanAdapter(actualProtocol, node); + + // Resolve inputs + const coin = this.resolveReference(node.inputs.coin_repay); + const receipt = this.resolveReference(node.inputs.receipt); + let borrowedAmount: bigint | undefined; if (borrowNode && borrowNode.type === "FLASH_BORROW") { borrowedAmount = BigInt(borrowNode.params.amount); @@ -227,10 +309,7 @@ export class TransactionBuilder { * Add DEX swap node */ private addDexSwap(node: DexSwapNode): void { - const adapter = this.dexAdapters.get(node.protocol); - if (!adapter) { - throw new Error(`No adapter found for DEX protocol: ${node.protocol}`); - } + const adapter = this.getDexAdapter(node.protocol); // Resolve input coin const coinIn = this.resolveReference(node.inputs.coin_in); diff --git a/backend/src/services/SuiClientService.ts b/backend/src/services/SuiClientService.ts new file mode 100644 index 0000000..d46a76d --- /dev/null +++ b/backend/src/services/SuiClientService.ts @@ -0,0 +1,35 @@ +import { SuiClient } from '@mysten/sui/client'; +import dotenv from 'dotenv'; + +dotenv.config(); + +let suiClientInstance: SuiClient | null = null; + +/** + * Get or create a singleton instance of SuiClient + */ +export function getSuiClient(): SuiClient { + if (!suiClientInstance) { + const network = process.env.SUI_NETWORK || 'mainnet'; + + let url: string; + switch (network) { + case 'mainnet': + url = 'https://fullnode.mainnet.sui.io:443'; + break; + case 'testnet': + url = 'https://fullnode.testnet.sui.io:443'; + break; + case 'devnet': + url = 'https://fullnode.devnet.sui.io:443'; + break; + default: + url = 'https://fullnode.mainnet.sui.io:443'; + } + + suiClientInstance = new SuiClient({ url }); + console.log(`✅ SuiClient initialized for ${network}`); + } + + return suiClientInstance; +} diff --git a/backend/src/services/UserDataService.ts b/backend/src/services/UserDataService.ts new file mode 100644 index 0000000..13cb20b --- /dev/null +++ b/backend/src/services/UserDataService.ts @@ -0,0 +1,362 @@ +import { SuiClient } from '@mysten/sui/client'; +import { Transaction } from '@mysten/sui/transactions'; +import { getAdminSigner, PACKAGE_ID } from '../config/admin'; + +/** + * Service to interact with the user_data smart contract + * Replaces localStorage usage for decentralized data storage + */ +export class UserDataService { + private suiClient: SuiClient; + + constructor(suiClient: SuiClient) { + this.suiClient = suiClient; + } + + /** + * Build transaction to create a new UserDataStorage object for a user + */ + async buildCreateStorageTransaction(userAddress: string): Promise { + const tx = new Transaction(); + tx.setSender(userAddress); + + tx.moveCall({ + target: `${PACKAGE_ID}::user_data::create_storage`, + arguments: [], + }); + + const txBytes = await tx.build({ client: this.suiClient }); + return txBytes; + } + + /** + * Build transaction to save a new strategy + */ + async buildSaveStrategyTransaction( + userAddress: string, + storageObjectId: string, + name: string, + description: string, + strategyJson: string + ): Promise { + const tx = new Transaction(); + tx.setSender(userAddress); + + tx.moveCall({ + target: `${PACKAGE_ID}::user_data::save_strategy`, + arguments: [ + tx.object(storageObjectId), + tx.pure.string(name), + tx.pure.string(description), + tx.pure.string(strategyJson), + ], + }); + + const txBytes = await tx.build({ client: this.suiClient }); + return txBytes; + } + + /** + * Build transaction to update existing strategy + */ + async buildUpdateStrategyTransaction( + userAddress: string, + storageObjectId: string, + strategyId: number, + name: string, + description: string, + strategyJson: string + ): Promise { + const tx = new Transaction(); + tx.setSender(userAddress); + + tx.moveCall({ + target: `${PACKAGE_ID}::user_data::update_strategy`, + arguments: [ + tx.object(storageObjectId), + tx.pure.u64(strategyId), + tx.pure.string(name), + tx.pure.string(description), + tx.pure.string(strategyJson), + ], + }); + + const txBytes = await tx.build({ client: this.suiClient }); + return txBytes; + } + + /** + * Build transaction to delete a strategy + */ + async buildDeleteStrategyTransaction( + userAddress: string, + storageObjectId: string, + strategyId: number + ): Promise { + const tx = new Transaction(); + tx.setSender(userAddress); + + tx.moveCall({ + target: `${PACKAGE_ID}::user_data::delete_strategy`, + arguments: [ + tx.object(storageObjectId), + tx.pure.u64(strategyId), + ], + }); + + const txBytes = await tx.build({ client: this.suiClient }); + return txBytes; + } + + /** + * Build transaction to record a workflow execution + */ + async buildRecordExecutionTransaction( + userAddress: string, + storageObjectId: string, + workflowName: string, + workflowId: string, + status: string, + txDigest: string, + gasUsed: number, + resultData: string + ): Promise { + const tx = new Transaction(); + tx.setSender(userAddress); + + tx.moveCall({ + target: `${PACKAGE_ID}::user_data::record_execution`, + arguments: [ + tx.object(storageObjectId), + tx.pure.string(workflowName), + tx.pure.string(workflowId), + tx.pure.string(status), + tx.pure.string(txDigest), + tx.pure.u64(gasUsed), + tx.pure.string(resultData), + ], + }); + + const txBytes = await tx.build({ client: this.suiClient }); + return txBytes; + } + + /** + * Build transaction to clear execution history + */ + async buildClearHistoryTransaction( + userAddress: string, + storageObjectId: string + ): Promise { + const tx = new Transaction(); + tx.setSender(userAddress); + + tx.moveCall({ + target: `${PACKAGE_ID}::user_data::clear_execution_history`, + arguments: [tx.object(storageObjectId)], + }); + + const txBytes = await tx.build({ client: this.suiClient }); + return txBytes; + } + + /** + * Get user's storage object ID by querying owned objects + */ + async getUserStorageObjectId(userAddress: string): Promise { + try { + const ownedObjects = await this.suiClient.getOwnedObjects({ + owner: userAddress, + filter: { + StructType: `${PACKAGE_ID}::user_data::UserDataStorage`, + }, + options: { + showContent: true, + showType: true, + }, + }); + + if (ownedObjects.data.length === 0) { + return null; + } + + // Return the first storage object (users should only have one) + return ownedObjects.data[0].data?.objectId || null; + } catch (error) { + console.error('Error getting user storage object:', error); + return null; + } + } + + /** + * Get all saved strategies for a user + */ + async getUserStrategies(storageObjectId: string): Promise { + try { + const storageObject = await this.suiClient.getObject({ + id: storageObjectId, + options: { + showContent: true, + }, + }); + + if (storageObject.data?.content?.dataType !== 'moveObject') { + return []; + } + + const fields = (storageObject.data.content as any).fields; + const strategyCounter = parseInt(fields.strategy_counter || '0'); + const savedStrategiesTableId = fields.saved_strategies?.fields?.id?.id; + + if (!savedStrategiesTableId) { + return []; + } + + // Query all strategies from the table + const strategies: any[] = []; + for (let i = 0; i < strategyCounter; i++) { + try { + const dynamicField = await this.suiClient.getDynamicFieldObject({ + parentId: savedStrategiesTableId, + name: { + type: 'u64', + value: i.toString(), + }, + }); + + if (dynamicField.data?.content?.dataType === 'moveObject') { + const strategyFields = (dynamicField.data.content as any).fields.value.fields; + strategies.push({ + id: parseInt(strategyFields.id), + name: strategyFields.name, + description: strategyFields.description, + strategyJson: JSON.parse(strategyFields.strategy_json), + createdAt: parseInt(strategyFields.created_at), + updatedAt: parseInt(strategyFields.updated_at), + }); + } + } catch (error) { + // Strategy might have been deleted, skip + continue; + } + } + + return strategies; + } catch (error) { + console.error('Error getting user strategies:', error); + return []; + } + } + + /** + * Get execution history for a user + */ + async getUserExecutionHistory(storageObjectId: string): Promise { + try { + const storageObject = await this.suiClient.getObject({ + id: storageObjectId, + options: { + showContent: true, + }, + }); + + if (storageObject.data?.content?.dataType !== 'moveObject') { + return []; + } + + const fields = (storageObject.data.content as any).fields; + const executionCounter = parseInt(fields.execution_counter || '0'); + const executionHistoryTableId = fields.execution_history?.fields?.id?.id; + + if (!executionHistoryTableId) { + return []; + } + + // Query all executions from the table + const executions: any[] = []; + for (let i = 0; i < executionCounter; i++) { + try { + const dynamicField = await this.suiClient.getDynamicFieldObject({ + parentId: executionHistoryTableId, + name: { + type: 'u64', + value: i.toString(), + }, + }); + + if (dynamicField.data?.content?.dataType === 'moveObject') { + const executionFields = (dynamicField.data.content as any).fields.value.fields; + executions.push({ + id: parseInt(executionFields.id), + workflowName: executionFields.workflow_name, + workflowId: executionFields.workflow_id, + status: executionFields.status, + txDigest: executionFields.tx_digest, + timestamp: parseInt(executionFields.timestamp), + gasUsed: parseInt(executionFields.gas_used), + resultData: JSON.parse(executionFields.result_data || '{}'), + }); + } + } catch (error) { + // Execution might have been deleted, skip + continue; + } + } + + return executions.sort((a, b) => b.timestamp - a.timestamp); + } catch (error) { + console.error('Error getting execution history:', error); + return []; + } + } + + /** + * Get a specific strategy by ID + */ + async getStrategy(storageObjectId: string, strategyId: number): Promise { + try { + const storageObject = await this.suiClient.getObject({ + id: storageObjectId, + options: { + showContent: true, + }, + }); + + if (storageObject.data?.content?.dataType !== 'moveObject') { + return null; + } + + const fields = (storageObject.data.content as any).fields; + const savedStrategiesTableId = fields.saved_strategies?.fields?.id?.id; + + if (!savedStrategiesTableId) { + return null; + } + + const dynamicField = await this.suiClient.getDynamicFieldObject({ + parentId: savedStrategiesTableId, + name: { + type: 'u64', + value: strategyId.toString(), + }, + }); + + if (dynamicField.data?.content?.dataType !== 'moveObject') { + return null; + } + + const strategyFields = (dynamicField.data.content as any).fields.value.fields; + return { + id: parseInt(strategyFields.id), + name: strategyFields.name, + description: strategyFields.description, + strategyJson: JSON.parse(strategyFields.strategy_json), + createdAt: parseInt(strategyFields.created_at), + updatedAt: parseInt(strategyFields.updated_at), + }; + } catch (error) { + console.error('Error getting strategy:', error); + return null; + } + } +} diff --git a/backend/src/types/strategy.ts b/backend/src/types/strategy.ts index 0094e30..f58c07b 100644 --- a/backend/src/types/strategy.ts +++ b/backend/src/types/strategy.ts @@ -15,6 +15,8 @@ export type NodeType = | "DEX_SWAP" | "COIN_SPLIT" | "COIN_MERGE" + | "PERP_OPEN" + | "PERP_CLOSE" | "CUSTOM"; export type Protocol = @@ -25,6 +27,10 @@ export type Protocol = | "CETUS" | "TURBOS" | "AFTERMATH_ROUTER" + | "AFTERMATH_PERP" + | "HYPERSUI" + | "ASTROS" + | "PERPSEA" | "NATIVE" // For built-in Sui operations (split, merge) | "CUSTOM"; // For user-defined custom Move calls @@ -91,7 +97,7 @@ export interface BaseNode { export interface FlashBorrowNode extends BaseNode { type: "FLASH_BORROW"; - protocol: "NAVI" | "DEEPBOOK_V3" | "BUCKET" | "SCALLOP"; + protocol: "NAVI" | "DEEPBOOK_V3" | "BUCKET" | "SCALLOP" | "TURBOS"; params: FlashBorrowParams; outputs: [NodeOutput, NodeOutput]; // [coin, receipt] } @@ -99,8 +105,11 @@ export interface FlashBorrowNode extends BaseNode { export interface FlashBorrowParams { asset: string; // Coin type: "0x2::sui::SUI" amount: string; // Amount as string (u64) - pool_id?: string; // Required for DeepBook, optional for others + pool_id?: string; // Required for DeepBook and Turbos, optional for others asset_type?: AssetType; // Required for DeepBook (BASE or QUOTE) + coin_type_a?: string; // Required for Turbos: first coin type in pool + coin_type_b?: string; // Required for Turbos: second coin type in pool + recipient?: string; // Optional recipient address (defaults to sender) } // ---------------------------------------------------------------------------- @@ -109,15 +118,17 @@ export interface FlashBorrowParams { export interface FlashRepayNode extends BaseNode { type: "FLASH_REPAY"; - protocol: "NAVI" | "DEEPBOOK_V3" | "BUCKET" | "SCALLOP"; + protocol: "NAVI" | "DEEPBOOK_V3" | "BUCKET" | "SCALLOP" | "TURBOS"; params: FlashRepayParams; inputs: FlashRepayInputs; } export interface FlashRepayParams { asset: string; // Must match borrow asset - pool_id?: string; // Required for DeepBook + pool_id?: string; // Required for DeepBook and Turbos asset_type?: AssetType; // Required for DeepBook + coin_type_a?: string; // Required for Turbos: first coin type in pool + coin_type_b?: string; // Required for Turbos: second coin type in pool } export interface FlashRepayInputs { @@ -164,6 +175,7 @@ export interface TurbosSwapParams { coin_type_a: string; coin_type_b: string; direction: Extract; + amount_mode: AmountMode; // EXACT_IN or EXACT_OUT amount: string; slippage_tolerance: string; } @@ -214,6 +226,54 @@ export interface CoinMergeInputs { merge_coins: string[]; // Array of coins to merge (same type) } +// ---------------------------------------------------------------------------- +// PERP_OPEN Node (Open Perpetual Position) +// ---------------------------------------------------------------------------- + +export interface PerpOpenNode extends BaseNode { + type: "PERP_OPEN"; + protocol: "AFTERMATH_PERP" | "HYPERSUI" | "ASTROS" | "PERPSEA"; + params: PerpOpenParams; + inputs: PerpOpenInputs; + outputs: [NodeOutput]; // [position_id or receipt] +} + +export interface PerpOpenParams { + market_id: string; // Market identifier (e.g., "SUI/USDC") + direction: "LONG" | "SHORT"; // Position direction + size: string; // Position size (in base asset units) + leverage?: string; // Leverage multiplier (e.g., "10" for 10x) + collateral: string; // Collateral amount + collateral_type: string; // Collateral coin type + slippage_tolerance?: string; // Slippage tolerance (e.g., "0.01" for 1%) +} + +export interface PerpOpenInputs { + collateral: string; // Reference: "node_id.output_id" +} + +// ---------------------------------------------------------------------------- +// PERP_CLOSE Node (Close Perpetual Position) +// ---------------------------------------------------------------------------- + +export interface PerpCloseNode extends BaseNode { + type: "PERP_CLOSE"; + protocol: "AFTERMATH_PERP" | "HYPERSUI" | "ASTROS" | "PERPSEA"; + params: PerpCloseParams; + inputs: PerpCloseInputs; + outputs: [NodeOutput]; // [collateral_returned] +} + +export interface PerpCloseParams { + market_id: string; // Market identifier (must match open) + position_id?: string; // Position ID (if protocol uses position objects) + size?: string; // Partial close size (optional, if not provided closes full position) +} + +export interface PerpCloseInputs { + position: string; // Reference: "node_id.output_id" (position from PERP_OPEN) +} + // ---------------------------------------------------------------------------- // CUSTOM Node (User-Defined Move Calls) // ---------------------------------------------------------------------------- @@ -252,6 +312,8 @@ export type Node = | DexSwapNode | CoinSplitNode | CoinMergeNode + | PerpOpenNode + | PerpCloseNode | CustomNode; // ============================================================================ diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 5908598..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,26 +0,0 @@ -services: - backend: - build: - context: ./backend - dockerfile: Dockerfile - ports: - - "8000:3000" - environment: - - PORT=3000 - # Add other environment variables here if needed - restart: unless-stopped - - frontend: - build: - context: ./frontend - dockerfile: Dockerfile - ports: - - "3000:3000" # Map host 3000 to container 3000 (Next.js default) - environment: - - WATCHPACK_POLLING=true - volumes: - - ./frontend:/app - - /app/node_modules - command: pnpm dev - depends_on: - - backend diff --git a/frontend/docker-compose.dev.yml b/frontend/docker-compose.dev.yml new file mode 100644 index 0000000..411bc57 --- /dev/null +++ b/frontend/docker-compose.dev.yml @@ -0,0 +1,4 @@ +services: + frontend: + ports: + - "3000:3000" diff --git a/frontend/docker-compose.yml b/frontend/docker-compose.yml new file mode 100644 index 0000000..179c5a4 --- /dev/null +++ b/frontend/docker-compose.yml @@ -0,0 +1,14 @@ +services: + frontend: + build: + context: . + dockerfile: Dockerfile + env_file: + - .env + environment: + - WATCHPACK_POLLING=true + volumes: + - .:/app + - /app/node_modules + command: pnpm dev + restart: unless-stopped diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 8d29f54..9cf9336 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,28 +10,28 @@ importers: dependencies: '@emotion/react': specifier: ^11.14.0 - version: 11.14.0(@types/react@19.2.6)(react@19.2.0) + version: 11.14.0(@types/react@19.2.7)(react@19.2.0) '@emotion/styled': specifier: ^11.14.1 - version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0) + version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0) '@mui/icons-material': specifier: ^7.3.5 - version: 7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.6)(react@19.2.0) + version: 7.3.6(@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.7)(react@19.2.0) '@mui/material': specifier: ^7.3.5 - version: 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@mysten/dapp-kit': specifier: ^0.19.9 - version: 0.19.9(@tanstack/react-query@5.90.10(react@19.2.0))(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(babel-plugin-macros@3.1.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + version: 0.19.11(@tanstack/react-query@5.90.12(react@19.2.0))(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(babel-plugin-macros@3.1.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) '@mysten/enoki': specifier: ^0.12.12 - version: 0.12.12(@types/react@19.2.6)(react@19.2.0)(typescript@5.9.3) + version: 0.12.15(@types/react@19.2.7)(react@19.2.0)(typescript@5.9.3) '@mysten/sui': specifier: ^1.45.0 - version: 1.45.0(typescript@5.9.3) + version: 1.45.2(typescript@5.9.3) '@tanstack/react-query': specifier: ^5.90.10 - version: 5.90.10(react@19.2.0) + version: 5.90.12(react@19.2.0) '@types/uuid': specifier: ^11.0.0 version: 11.0.0 @@ -40,7 +40,7 @@ importers: version: 2.1.1 framer-motion: specifier: ^12.23.24 - version: 12.23.24(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) lucide-react: specifier: ^0.554.0 version: 0.554.0(react@19.2.0) @@ -62,28 +62,28 @@ importers: devDependencies: '@tailwindcss/postcss': specifier: ^4 - version: 4.1.17 + version: 4.1.18 '@types/node': specifier: ^20 - version: 20.19.25 + version: 20.19.27 '@types/react': specifier: ^19 - version: 19.2.6 + version: 19.2.7 '@types/react-dom': specifier: ^19 - version: 19.2.3(@types/react@19.2.6) + version: 19.2.3(@types/react@19.2.7) babel-plugin-react-compiler: specifier: 1.0.0 version: 1.0.0 eslint: specifier: ^9 - version: 9.39.1(jiti@2.6.1) + version: 9.39.2(jiti@2.6.1) eslint-config-next: specifier: 16.0.3 - version: 16.0.3(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + version: 16.0.3(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) tailwindcss: specifier: ^4 - version: 4.1.17 + version: 4.1.18 typescript: specifier: ^5 version: 5.9.3 @@ -98,8 +98,8 @@ packages: graphql: optional: true - '@0no-co/graphqlsp@1.15.1': - resolution: {integrity: sha512-UBDBuVGpX5Ti0PjGnSAzkMG04psNYxKfJ+1bgF8HFPfHHpKNVl4GULHSNW0GTOngcYCYA70c+InoKw0qjHwmVQ==} + '@0no-co/graphqlsp@1.15.2': + resolution: {integrity: sha512-Ys031WnS3sTQQBtRTkQsYnw372OlW72ais4sp0oh2UMPRNyxxnq85zRfU4PIdoy9kWriysPT5BYAkgIxhbonFA==} peerDependencies: graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 typescript: ^5.0.0 @@ -264,12 +264,12 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -324,8 +324,8 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@grpc/grpc-js@1.14.1': - resolution: {integrity: sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ==} + '@grpc/grpc-js@1.14.3': + resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==} engines: {node: '>=12.10.0'} '@grpc/proto-loader@0.7.15': @@ -522,8 +522,8 @@ packages: '@ledgerhq/hw-transport@6.31.13': resolution: {integrity: sha512-MrJRDk74wY980ofiFPRpTHQBbRw1wDuKbdag1zqlO1xtJglymwwY03K2kvBNvkm1RTSCPUp/nAoNG+WThZuuew==} - '@ledgerhq/ledger-cal-service@1.9.0': - resolution: {integrity: sha512-OuM5+pOcRYDs25VgTD1f5hzhc154g6Oh8dL3M+77MtpQOO3XCnLuWe7eFNS28r6jWrojN1v8I1aTYiVJh+vdeg==} + '@ledgerhq/ledger-cal-service@1.9.1': + resolution: {integrity: sha512-QR5rdjrLn7qXPZj2Le+BSuLGUsgYFdOkHipm4Ky4WJOT8hm96kaBcQOJhqO4JTfo6bYvInkcfVaDwKytq2B13w==} '@ledgerhq/ledger-trust-service@0.3.14': resolution: {integrity: sha512-Q83QXnLDFIDnm2Bv4WgWilurE1WGSX7gGaMEhVxb/BQXH1O78QlNgNRys8K35wnG7BxYKjBJGMoK9U6JdWwW2Q==} @@ -531,14 +531,14 @@ packages: '@ledgerhq/live-env@2.18.0': resolution: {integrity: sha512-wasAK0IPyyX4ZAU0FLTZUsRzxJfdHlewyhkbz3HryYKbNofuMRwIWyaZ2xhpXXjEdP6qHb9HzewAZevjSn7DLQ==} - '@ledgerhq/live-env@2.21.0': - resolution: {integrity: sha512-A/+lCW6HNE9TWMCVo3dluZ0qTp9l+LMV9vH62SXqeHNYL3MqE/gsdZ1sZEMimLx8fURkCIKlHdNd1lN/tY5t/A==} + '@ledgerhq/live-env@2.22.0': + resolution: {integrity: sha512-zOtxXQl0stQpNp/01nvClMyAdc5WLY8b4vAeP8KJvvoDPCY9VfKLNGvjfU7r/AGsTTmJqy8DOSlboQ24CNd+zg==} '@ledgerhq/live-network@2.0.19': resolution: {integrity: sha512-KVdqFDQcqEUNoC/TnaOJeo/I7TY39lQhzcVlnkAt2jbp5Xfv5YNuazL9lId5+bBb28jNPAv5uNJSlgvzpSSNIg==} - '@ledgerhq/live-network@2.1.1': - resolution: {integrity: sha512-58YMh/dUyntKubtzOsToyBjVakIM58h3k7nEyrUXY7UpO0VWY1TvSF0D4mdPGVpbwNs9C2KP3Lf/kowbAYpVJA==} + '@ledgerhq/live-network@2.1.2': + resolution: {integrity: sha512-Sz1kgRcHhD5y/cIQrNi69JDJf9clLylTzIBy33im0Bpfeq/Ko1xL1YBzzSeoHMPezrxwhHVO5Q1M1B5HBczSyw==} '@ledgerhq/live-promise@0.1.1': resolution: {integrity: sha512-oFo6KUZIdFEndp2uokSU9XE9UCT/7NLOOKa1nQUfHhAfyKxRWqBlZOGRuivM/BIwKm0IUFa3+HE7uxK497kYPg==} @@ -546,27 +546,27 @@ packages: '@ledgerhq/logs@6.13.0': resolution: {integrity: sha512-4+qRW2Pc8V+btL0QEmdB2X+uyx0kOWMWE1/LWsq5sZy3Q5tpi4eItJS6mB0XL3wGW59RQ+8bchNQQ1OW/va8Og==} - '@mui/core-downloads-tracker@7.3.5': - resolution: {integrity: sha512-kOLwlcDPnVz2QMhiBv0OQ8le8hTCqKM9cRXlfVPL91l3RGeOsxrIhNRsUt3Xb8wb+pTVUolW+JXKym93vRKxCw==} + '@mui/core-downloads-tracker@7.3.6': + resolution: {integrity: sha512-QaYtTHlr8kDFN5mE1wbvVARRKH7Fdw1ZuOjBJcFdVpfNfRYKF3QLT4rt+WaB6CKJvpqxRsmEo0kpYinhH5GeHg==} - '@mui/icons-material@7.3.5': - resolution: {integrity: sha512-LciL1GLMZ+VlzyHAALSVAR22t8IST4LCXmljcUSx2NOutgO2XnxdIp8ilFbeNf9wpo0iUFbAuoQcB7h+HHIf3A==} + '@mui/icons-material@7.3.6': + resolution: {integrity: sha512-0FfkXEj22ysIq5pa41A2NbcAhJSvmcZQ/vcTIbjDsd6hlslG82k5BEBqqS0ZJprxwIL3B45qpJ+bPHwJPlF7uQ==} engines: {node: '>=14.0.0'} peerDependencies: - '@mui/material': ^7.3.5 + '@mui/material': ^7.3.6 '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true - '@mui/material@7.3.5': - resolution: {integrity: sha512-8VVxFmp1GIm9PpmnQoCoYo0UWHoOrdA57tDL62vkpzEgvb/d71Wsbv4FRg7r1Gyx7PuSo0tflH34cdl/NvfHNQ==} + '@mui/material@7.3.6': + resolution: {integrity: sha512-R4DaYF3dgCQCUAkr4wW1w26GHXcf5rCmBRHVBuuvJvaGLmZdD8EjatP80Nz5JCw0KxORAzwftnHzXVnjR8HnFw==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.5.0 '@emotion/styled': ^11.3.0 - '@mui/material-pigment-css': ^7.3.5 + '@mui/material-pigment-css': ^7.3.6 '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -580,8 +580,8 @@ packages: '@types/react': optional: true - '@mui/private-theming@7.3.5': - resolution: {integrity: sha512-cTx584W2qrLonwhZLbEN7P5pAUu0nZblg8cLBlTrZQ4sIiw8Fbvg7GvuphQaSHxPxrCpa7FDwJKtXdbl2TSmrA==} + '@mui/private-theming@7.3.6': + resolution: {integrity: sha512-Ws9wZpqM+FlnbZXaY/7yvyvWQo1+02Tbx50mVdNmzWEi51C51y56KAbaDCYyulOOBL6BJxuaqG8rNNuj7ivVyw==} engines: {node: '>=14.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -590,8 +590,8 @@ packages: '@types/react': optional: true - '@mui/styled-engine@7.3.5': - resolution: {integrity: sha512-zbsZ0uYYPndFCCPp2+V3RLcAN6+fv4C8pdwRx6OS3BwDkRCN8WBehqks7hWyF3vj1kdQLIWrpdv/5Y0jHRxYXQ==} + '@mui/styled-engine@7.3.6': + resolution: {integrity: sha512-+wiYbtvj+zyUkmDB+ysH6zRjuQIJ+CM56w0fEXV+VDNdvOuSywG+/8kpjddvvlfMLsaWdQe5oTuYGBcodmqGzQ==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.4.1 @@ -603,8 +603,8 @@ packages: '@emotion/styled': optional: true - '@mui/system@7.3.5': - resolution: {integrity: sha512-yPaf5+gY3v80HNkJcPi6WT+r9ebeM4eJzrREXPxMt7pNTV/1eahyODO4fbH3Qvd8irNxDFYn5RQ3idHW55rA6g==} + '@mui/system@7.3.6': + resolution: {integrity: sha512-8fehAazkHNP1imMrdD2m2hbA9sl7Ur6jfuNweh5o4l9YPty4iaZzRXqYvBCWQNwFaSHmMEj2KPbyXGp7Bt73Rg==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -619,16 +619,16 @@ packages: '@types/react': optional: true - '@mui/types@7.4.8': - resolution: {integrity: sha512-ZNXLBjkPV6ftLCmmRCafak3XmSn8YV0tKE/ZOhzKys7TZXUiE0mZxlH8zKDo6j6TTUaDnuij68gIG+0Ucm7Xhw==} + '@mui/types@7.4.9': + resolution: {integrity: sha512-dNO8Z9T2cujkSIaCnWwprfeKmTWh97cnjkgmpFJ2sbfXLx8SMZijCYHOtP/y5nnUb/Rm2omxbDMmtUoSaUtKaw==} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true - '@mui/utils@7.3.5': - resolution: {integrity: sha512-jisvFsEC3sgjUjcPnR4mYfhzjCDIudttSGSbe1o/IXFNu0kZuR+7vqQI0jg8qtcVZBHWrwTfvAZj9MNMumcq1g==} + '@mui/utils@7.3.6': + resolution: {integrity: sha512-jn+Ba02O6PiFs7nKva8R2aJJ9kJC+3kQ2R0BbKNY3KQQ36Qng98GnPRFTlbwYTdMD6hLEBKaMLUktyg/rTfd2w==} engines: {node: '>=14.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -640,14 +640,14 @@ packages: '@mysten/bcs@1.9.2': resolution: {integrity: sha512-kBk5xrxV9OWR7i+JhL/plQrgQ2/KJhB2pB5gj+w6GXhbMQwS3DPpOvi/zN0Tj84jwPvHMllpEl0QHj6ywN7/eQ==} - '@mysten/dapp-kit@0.19.9': - resolution: {integrity: sha512-tQmAp9vTpNkN+t8kB5RNuNy7hPDQMDBaWA+IlxeZdTk9ovYzfj0rDWoTpbWzom8zSGJRayKbHKIbq04Jtw2pZg==} + '@mysten/dapp-kit@0.19.11': + resolution: {integrity: sha512-b0poDfJVTzmIYtDRT06KFxzer4xAN+KMvIJuZ8bIMXAOfV7a17fMEJ94HI1nXCflQGGbx+BKsIKcb93ptwPgag==} peerDependencies: '@tanstack/react-query': ^5.0.0 react: '*' - '@mysten/enoki@0.12.12': - resolution: {integrity: sha512-E/ugaZpc91LC3v9jG3T2ZV5+Q3/jN1mQi74yxlBH2J1Y1SrLTPd47Hf4mlR5n3gXbzMmDhRj9HCpRhHWmii0Uw==} + '@mysten/enoki@0.12.15': + resolution: {integrity: sha512-pNjvF7wtxOPvUEI4nSieiEWNZyHgSvO+nYj/ZYC5gIkN7BeGMoaPPMcybiVkol00IEahkcYnzJ2eCLRcqVn+WQ==} peerDependencies: '@types/react': '>=17.0.0' react: '>=17.0.0' @@ -660,25 +660,25 @@ packages: '@mysten/ledgerjs-hw-app-sui@0.7.0': resolution: {integrity: sha512-Z2ydxg35U2tk3pThsWzPewticDnHWqAeLlh2Vp4NhYU/YFyoVxK9obh21pnvU/hmRzL+BRpYlwbX5Nuo77+lwA==} - '@mysten/signers@0.6.0': - resolution: {integrity: sha512-aZ2nH+rcDchx+gv5v9otVbMFFVsOuc9i0xUINy/glUUcTO9CXwdBeAN3sYgxTTMCPFQ0guMBZZwfyDg4/2FEYA==} + '@mysten/signers@0.6.2': + resolution: {integrity: sha512-TFdsoixjly/PHSRDF93wAl/6+cYGS/4J2PCg8lLoxmrLQZCDqIKYwP/dy1243qkXzyeN9d0Ri5IJvozcDpoY/g==} engines: {node: '>=20'} - '@mysten/slush-wallet@0.2.10': - resolution: {integrity: sha512-+Km3FSGMJ1RoRMijpiq9YFL0rocKPAsBolUdd8pjI7PR9wYgA/wTRV5gJmzVLgJTGfkEkJOqdOccg51XGw1NBw==} + '@mysten/slush-wallet@0.2.12': + resolution: {integrity: sha512-OVIQbADqUVZCTps3MGvVI90nczTbwepAb75x+jZuH2W2p8lXoYIuvuuP4KlwwalR9QgpqOqptdpYFVAKi8ncLQ==} - '@mysten/sui@1.45.0': - resolution: {integrity: sha512-WYpZmrGWE/FeQaRhYrAYpHcHallj4BFIfSmjq6BkODzEhP9HfMplQAtd8AmtctwO7cpHJ+BjPUOguEAcf1gU4g==} + '@mysten/sui@1.45.2': + resolution: {integrity: sha512-gftf7fNpFSiXyfXpbtP2afVEnhc7p2m/MEYc/SO5pov92dacGKOpQIF7etZsGDI1Wvhv+dpph+ulRNpnYSs7Bg==} engines: {node: '>=18'} '@mysten/utils@0.2.0': resolution: {integrity: sha512-CM6kJcJHX365cK6aXfFRLBiuyXc5WSBHQ43t94jqlCAIRw8umgNcTb5EnEA9n31wPAQgLDGgbG/rCUISCTJ66w==} - '@mysten/wallet-standard@0.19.7': - resolution: {integrity: sha512-p0W1WUkXqUXtmCL8wlkjToSXoqTj5zRH2dVI+yhxrFCiR236SdgZ+BGIZrx0yaE/igzttsTaVztbA7jMc8t6oQ==} + '@mysten/wallet-standard@0.19.9': + resolution: {integrity: sha512-jHFt+62os7x7y+4ZVMLck8WSanEO9b8deCD+VApUQkdAHA99TuxbREaujQTjnGQN5DaGEz8wQgeBPqxRY/vKQA==} - '@mysten/window-wallet-core@0.1.0': - resolution: {integrity: sha512-QVFthpAv+HJBqoLjsE6gZylE28lrzEd/C68S5tyOCpRV6T3O+P3LCcFmNkLK8gNM6DJNHc/GZgM3f4rTNGOfAQ==} + '@mysten/window-wallet-core@0.1.1': + resolution: {integrity: sha512-TboJvuqXvJbKy0sqK72kR3RXp7SLkgNfEaNsKWsSUwD+wV9+h/S3wtO+E+yFmPgHgOr8c7HJIQLAdunMssKZtg==} '@nanostores/react@1.0.0': resolution: {integrity: sha512-eDduyNy+lbQJMg6XxZ/YssQqF6b4OXMFEZMYKPJCCmBevp1lg0g+4ZRi94qGHirMtsNfAWKNwsjOhC+q1gvC+A==} @@ -1113,65 +1113,65 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@tailwindcss/node@4.1.17': - resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} + '@tailwindcss/node@4.1.18': + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} - '@tailwindcss/oxide-android-arm64@4.1.17': - resolution: {integrity: sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==} + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.17': - resolution: {integrity: sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==} + '@tailwindcss/oxide-darwin-arm64@4.1.18': + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.17': - resolution: {integrity: sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==} + '@tailwindcss/oxide-darwin-x64@4.1.18': + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.17': - resolution: {integrity: sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==} + '@tailwindcss/oxide-freebsd-x64@4.1.18': + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': - resolution: {integrity: sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': - resolution: {integrity: sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.17': - resolution: {integrity: sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.17': - resolution: {integrity: sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.17': - resolution: {integrity: sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==} + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.17': - resolution: {integrity: sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==} + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -1182,30 +1182,30 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': - resolution: {integrity: sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.17': - resolution: {integrity: sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.17': - resolution: {integrity: sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==} + '@tailwindcss/oxide@4.1.18': + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} engines: {node: '>= 10'} - '@tailwindcss/postcss@4.1.17': - resolution: {integrity: sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==} + '@tailwindcss/postcss@4.1.18': + resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==} - '@tanstack/query-core@5.90.10': - resolution: {integrity: sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==} + '@tanstack/query-core@5.90.12': + resolution: {integrity: sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg==} - '@tanstack/react-query@5.90.10': - resolution: {integrity: sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==} + '@tanstack/react-query@5.90.12': + resolution: {integrity: sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==} peerDependencies: react: ^18 || ^19 @@ -1231,8 +1231,8 @@ packages: '@types/long@4.0.2': resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} - '@types/node@20.19.25': - resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==} + '@types/node@20.19.27': + resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1250,8 +1250,8 @@ packages: peerDependencies: '@types/react': '*' - '@types/react@19.2.6': - resolution: {integrity: sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==} + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} '@types/request@2.48.13': resolution: {integrity: sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==} @@ -1263,63 +1263,63 @@ packages: resolution: {integrity: sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==} deprecated: This is a stub types definition. uuid provides its own type definitions, so you do not need this installed. - '@typescript-eslint/eslint-plugin@8.47.0': - resolution: {integrity: sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==} + '@typescript-eslint/eslint-plugin@8.49.0': + resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.47.0 + '@typescript-eslint/parser': ^8.49.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.47.0': - resolution: {integrity: sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==} + '@typescript-eslint/parser@8.49.0': + resolution: {integrity: sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.47.0': - resolution: {integrity: sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==} + '@typescript-eslint/project-service@8.49.0': + resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.47.0': - resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} + '@typescript-eslint/scope-manager@8.49.0': + resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.47.0': - resolution: {integrity: sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==} + '@typescript-eslint/tsconfig-utils@8.49.0': + resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.47.0': - resolution: {integrity: sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==} + '@typescript-eslint/type-utils@8.49.0': + resolution: {integrity: sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.47.0': - resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} + '@typescript-eslint/types@8.49.0': + resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.47.0': - resolution: {integrity: sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==} + '@typescript-eslint/typescript-estree@8.49.0': + resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.47.0': - resolution: {integrity: sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==} + '@typescript-eslint/utils@8.49.0': + resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.47.0': - resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} + '@typescript-eslint/visitor-keys@8.49.0': + resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -1417,8 +1417,8 @@ packages: cpu: [x64] os: [win32] - '@vanilla-extract/css@1.17.4': - resolution: {integrity: sha512-m3g9nQDWPtL+sTFdtCGRMI1Vrp86Ay4PBYq1Bo7Bnchj5ElNtAJpOqD+zg+apthVA4fB7oVpMWNjwpa6ElDWFQ==} + '@vanilla-extract/css@1.17.5': + resolution: {integrity: sha512-u29cUVL5Z2qjJ2Eh8pusT1ToGtTeA4eb/y0ygaw2vWv9XFQSixtkBYEsVkrJExSI/0+SR1g8n5NYas4KlWOdfA==} '@vanilla-extract/dynamic@2.1.5': resolution: {integrity: sha512-QGIFGb1qyXQkbzx6X6i3+3LMc/iv/ZMBttMBL+Wm/DetQd36KsKsFg5CtH3qy+1hCA/5w93mEIIAiL4fkM8ycw==} @@ -1596,8 +1596,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.8.30: - resolution: {integrity: sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==} + baseline-browser-mapping@2.9.7: + resolution: {integrity: sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==} hasBin: true bignumber.js@9.3.1: @@ -1613,8 +1613,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.0: - resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1637,8 +1637,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001756: - resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==} + caniuse-lite@1.0.30001760: + resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1789,8 +1789,8 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - electron-to-chromium@1.5.259: - resolution: {integrity: sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1801,15 +1801,15 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.18.3: - resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + enhanced-resolve@5.18.4: + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -1820,8 +1820,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} engines: {node: '>= 0.4'} es-object-atoms@1.1.1: @@ -1934,8 +1934,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1982,10 +1982,6 @@ packages: resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -2050,8 +2046,8 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} - framer-motion@12.23.24: - resolution: {integrity: sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==} + framer-motion@12.23.26: + resolution: {integrity: sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -2158,9 +2154,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - graphql@16.12.0: resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} @@ -2371,8 +2364,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.1.2: - resolution: {integrity: sha512-MpcPtHLE5EmztuFIqB0vzHAWJPpmN1E6L4oo+kze56LIs3MyXIj9ZHMDxqOvkP38gBR7K1v3jqd4WU2+nrfONQ==} + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2417,8 +2410,8 @@ packages: jwa@2.0.1: resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - jws@4.0.0: - resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -2610,6 +2603,7 @@ packages: next@16.0.3: resolution: {integrity: sha512-Ka0/iNBblPFcIubTA1Jjh6gvwqfjrGq1Y2MTI5lbjeLIAfmC+p5bQmojpRZqgHHVu5cG4+qdIiwXiBSm/8lZ3w==} engines: {node: '>=20.9.0'} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/CVE-2025-66478 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -2777,8 +2771,8 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@19.2.0: - resolution: {integrity: sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==} + react-is@19.2.3: + resolution: {integrity: sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==} react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} @@ -2790,8 +2784,8 @@ packages: '@types/react': optional: true - react-remove-scroll@2.7.1: - resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} engines: {node: '>=10'} peerDependencies: '@types/react': '*' @@ -3026,8 +3020,8 @@ packages: tailwind-merge@3.4.0: resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} - tailwindcss@4.1.17: - resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} @@ -3080,8 +3074,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.47.0: - resolution: {integrity: sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==} + typescript-eslint@8.49.0: + resolution: {integrity: sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -3102,8 +3096,8 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + update-browserslist-db@1.2.2: + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -3151,8 +3145,13 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - valibot@0.36.0: - resolution: {integrity: sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==} + valibot@1.2.0: + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -3221,8 +3220,8 @@ packages: peerDependencies: zod: ^3.25.0 || ^4.0.0 - zod@4.1.12: - resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} zustand@4.5.7: resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} @@ -3245,7 +3244,7 @@ snapshots: optionalDependencies: graphql: 16.12.0 - '@0no-co/graphqlsp@1.15.1(graphql@16.12.0)(typescript@5.9.3)': + '@0no-co/graphqlsp@1.15.2(graphql@16.12.0)(typescript@5.9.3)': dependencies: '@gql.tada/internal': 1.0.8(graphql@16.12.0)(typescript@5.9.3) graphql: 16.12.0 @@ -3293,7 +3292,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.0 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -3403,7 +3402,7 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0)': + '@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 @@ -3415,7 +3414,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 transitivePeerDependencies: - supports-color @@ -3429,18 +3428,18 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)': + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@19.2.6)(react@19.2.0) + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.0) '@emotion/serialize': 1.3.3 '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.0) '@emotion/utils': 1.4.2 react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 transitivePeerDependencies: - supports-color @@ -3454,9 +3453,9 @@ snapshots: '@emotion/weak-memoize@0.4.0': {} - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2(jiti@2.6.1))': dependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -3477,7 +3476,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 debug: 4.4.3 @@ -3491,7 +3490,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.39.1': {} + '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -3524,9 +3523,9 @@ snapshots: - encoding - supports-color - '@gql.tada/cli-utils@1.7.2(@0no-co/graphqlsp@1.15.1(graphql@16.12.0)(typescript@5.9.3))(graphql@16.12.0)(typescript@5.9.3)': + '@gql.tada/cli-utils@1.7.2(@0no-co/graphqlsp@1.15.2(graphql@16.12.0)(typescript@5.9.3))(graphql@16.12.0)(typescript@5.9.3)': dependencies: - '@0no-co/graphqlsp': 1.15.1(graphql@16.12.0)(typescript@5.9.3) + '@0no-co/graphqlsp': 1.15.2(graphql@16.12.0)(typescript@5.9.3) '@gql.tada/internal': 1.0.8(graphql@16.12.0)(typescript@5.9.3) graphql: 16.12.0 typescript: 5.9.3 @@ -3541,7 +3540,7 @@ snapshots: dependencies: graphql: 16.12.0 - '@grpc/grpc-js@1.14.1': + '@grpc/grpc-js@1.14.3': dependencies: '@grpc/proto-loader': 0.8.0 '@js-sdsl/ordered-map': 4.4.2 @@ -3709,10 +3708,10 @@ snapshots: '@ledgerhq/logs': 6.13.0 events: 3.3.0 - '@ledgerhq/ledger-cal-service@1.9.0': + '@ledgerhq/ledger-cal-service@1.9.1': dependencies: - '@ledgerhq/live-env': 2.21.0 - '@ledgerhq/live-network': 2.1.1 + '@ledgerhq/live-env': 2.22.0 + '@ledgerhq/live-network': 2.1.2 transitivePeerDependencies: - debug @@ -3728,7 +3727,7 @@ snapshots: rxjs: 7.8.2 utility-types: 3.11.0 - '@ledgerhq/live-env@2.21.0': + '@ledgerhq/live-env@2.22.0': dependencies: rxjs: 7.8.2 utility-types: 3.11.0 @@ -3744,10 +3743,10 @@ snapshots: transitivePeerDependencies: - debug - '@ledgerhq/live-network@2.1.1': + '@ledgerhq/live-network@2.1.2': dependencies: '@ledgerhq/errors': 6.27.0 - '@ledgerhq/live-env': 2.21.0 + '@ledgerhq/live-env': 2.22.0 '@ledgerhq/live-promise': 0.1.1 '@ledgerhq/logs': 6.13.0 axios: 1.12.2 @@ -3761,47 +3760,47 @@ snapshots: '@ledgerhq/logs@6.13.0': {} - '@mui/core-downloads-tracker@7.3.5': {} + '@mui/core-downloads-tracker@7.3.6': {} - '@mui/icons-material@7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)': + '@mui/icons-material@7.3.6(@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@mui/material': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@mui/material': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@mui/core-downloads-tracker': 7.3.5 - '@mui/system': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0) - '@mui/types': 7.4.8(@types/react@19.2.6) - '@mui/utils': 7.3.5(@types/react@19.2.6)(react@19.2.0) + '@mui/core-downloads-tracker': 7.3.6 + '@mui/system': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0) + '@mui/types': 7.4.9(@types/react@19.2.7) + '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.0) '@popperjs/core': 2.11.8 - '@types/react-transition-group': 4.4.12(@types/react@19.2.6) + '@types/react-transition-group': 4.4.12(@types/react@19.2.7) clsx: 2.1.1 csstype: 3.2.3 prop-types: 15.8.1 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - react-is: 19.2.0 + react-is: 19.2.3 react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.6)(react@19.2.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0) - '@types/react': 19.2.6 + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0) + '@types/react': 19.2.7 - '@mui/private-theming@7.3.5(@types/react@19.2.6)(react@19.2.0)': + '@mui/private-theming@7.3.6(@types/react@19.2.7)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@mui/utils': 7.3.5(@types/react@19.2.6)(react@19.2.0) + '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.0) prop-types: 15.8.1 react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@mui/styled-engine@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(react@19.2.0)': + '@mui/styled-engine@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 '@emotion/cache': 11.14.0 @@ -3811,64 +3810,64 @@ snapshots: prop-types: 15.8.1 react: 19.2.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.6)(react@19.2.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0) + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0) - '@mui/system@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)': + '@mui/system@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@mui/private-theming': 7.3.5(@types/react@19.2.6)(react@19.2.0) - '@mui/styled-engine': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(react@19.2.0) - '@mui/types': 7.4.8(@types/react@19.2.6) - '@mui/utils': 7.3.5(@types/react@19.2.6)(react@19.2.0) + '@mui/private-theming': 7.3.6(@types/react@19.2.7)(react@19.2.0) + '@mui/styled-engine': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(react@19.2.0) + '@mui/types': 7.4.9(@types/react@19.2.7) + '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.0) clsx: 2.1.1 csstype: 3.2.3 prop-types: 15.8.1 react: 19.2.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.6)(react@19.2.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0) - '@types/react': 19.2.6 + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0) + '@types/react': 19.2.7 - '@mui/types@7.4.8(@types/react@19.2.6)': + '@mui/types@7.4.9(@types/react@19.2.7)': dependencies: '@babel/runtime': 7.28.4 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@mui/utils@7.3.5(@types/react@19.2.6)(react@19.2.0)': + '@mui/utils@7.3.6(@types/react@19.2.7)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@mui/types': 7.4.8(@types/react@19.2.6) + '@mui/types': 7.4.9(@types/react@19.2.7) '@types/prop-types': 15.7.15 clsx: 2.1.1 prop-types: 15.8.1 react: 19.2.0 - react-is: 19.2.0 + react-is: 19.2.3 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 '@mysten/bcs@1.9.2': dependencies: '@mysten/utils': 0.2.0 '@scure/base': 1.2.6 - '@mysten/dapp-kit@0.19.9(@tanstack/react-query@5.90.10(react@19.2.0))(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(babel-plugin-macros@3.1.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': + '@mysten/dapp-kit@0.19.11(@tanstack/react-query@5.90.12(react@19.2.0))(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(babel-plugin-macros@3.1.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': dependencies: - '@mysten/slush-wallet': 0.2.10(typescript@5.9.3) - '@mysten/sui': 1.45.0(typescript@5.9.3) + '@mysten/slush-wallet': 0.2.12(typescript@5.9.3) + '@mysten/sui': 1.45.2(typescript@5.9.3) '@mysten/utils': 0.2.0 - '@mysten/wallet-standard': 0.19.7(typescript@5.9.3) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.6)(react@19.2.0) - '@tanstack/react-query': 5.90.10(react@19.2.0) - '@vanilla-extract/css': 1.17.4(babel-plugin-macros@3.1.0) + '@mysten/wallet-standard': 0.19.9(typescript@5.9.3) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.7)(react@19.2.0) + '@tanstack/react-query': 5.90.12(react@19.2.0) + '@vanilla-extract/css': 1.17.5(babel-plugin-macros@3.1.0) '@vanilla-extract/dynamic': 2.1.5 - '@vanilla-extract/recipes': 0.5.7(@vanilla-extract/css@1.17.4(babel-plugin-macros@3.1.0)) + '@vanilla-extract/recipes': 0.5.7(@vanilla-extract/css@1.17.5(babel-plugin-macros@3.1.0)) clsx: 2.1.1 react: 19.2.0 - zustand: 4.5.7(@types/react@19.2.6)(react@19.2.0) + zustand: 4.5.7(@types/react@19.2.7)(react@19.2.0) transitivePeerDependencies: - '@gql.tada/svelte-support' - '@gql.tada/vue-support' @@ -3879,18 +3878,18 @@ snapshots: - react-dom - typescript - '@mysten/enoki@0.12.12(@types/react@19.2.6)(react@19.2.0)(typescript@5.9.3)': + '@mysten/enoki@0.12.15(@types/react@19.2.7)(react@19.2.0)(typescript@5.9.3)': dependencies: - '@mysten/signers': 0.6.0(typescript@5.9.3) - '@mysten/sui': 1.45.0(typescript@5.9.3) - '@mysten/wallet-standard': 0.19.7(typescript@5.9.3) + '@mysten/signers': 0.6.2(typescript@5.9.3) + '@mysten/sui': 1.45.2(typescript@5.9.3) + '@mysten/wallet-standard': 0.19.9(typescript@5.9.3) '@nanostores/react': 1.0.0(nanostores@1.1.0)(react@19.2.0) '@wallet-standard/ui': 1.0.1 idb-keyval: 6.2.2 mitt: 3.0.1 nanostores: 1.1.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 react: 19.2.0 transitivePeerDependencies: - '@gql.tada/svelte-support' @@ -3906,18 +3905,18 @@ snapshots: '@ledgerhq/errors': 6.27.0 '@ledgerhq/hw-bolos': 6.32.9 '@ledgerhq/hw-transport': 6.31.13 - '@ledgerhq/ledger-cal-service': 1.9.0 + '@ledgerhq/ledger-cal-service': 1.9.1 '@ledgerhq/ledger-trust-service': 0.3.14 fast-sha256: 1.3.0 semver: 7.7.3 transitivePeerDependencies: - debug - '@mysten/signers@0.6.0(typescript@5.9.3)': + '@mysten/signers@0.6.2(typescript@5.9.3)': dependencies: '@google-cloud/kms': 4.5.0 '@mysten/ledgerjs-hw-app-sui': 0.7.0 - '@mysten/sui': 1.45.0(typescript@5.9.3) + '@mysten/sui': 1.45.2(typescript@5.9.3) '@noble/curves': 1.9.4 '@noble/hashes': 1.8.0 asn1-ts: 8.0.5 @@ -3929,20 +3928,20 @@ snapshots: - supports-color - typescript - '@mysten/slush-wallet@0.2.10(typescript@5.9.3)': + '@mysten/slush-wallet@0.2.12(typescript@5.9.3)': dependencies: - '@mysten/sui': 1.45.0(typescript@5.9.3) + '@mysten/sui': 1.45.2(typescript@5.9.3) '@mysten/utils': 0.2.0 - '@mysten/wallet-standard': 0.19.7(typescript@5.9.3) - '@mysten/window-wallet-core': 0.1.0 + '@mysten/wallet-standard': 0.19.9(typescript@5.9.3) + '@mysten/window-wallet-core': 0.1.1(typescript@5.9.3) mitt: 3.0.1 - valibot: 0.36.0 + valibot: 1.2.0(typescript@5.9.3) transitivePeerDependencies: - '@gql.tada/svelte-support' - '@gql.tada/vue-support' - typescript - '@mysten/sui@1.45.0(typescript@5.9.3)': + '@mysten/sui@1.45.2(typescript@5.9.3)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) '@mysten/bcs': 1.9.2 @@ -3958,7 +3957,7 @@ snapshots: gql.tada: 1.9.0(graphql@16.12.0)(typescript@5.9.3) graphql: 16.12.0 poseidon-lite: 0.2.1 - valibot: 0.36.0 + valibot: 1.2.0(typescript@5.9.3) transitivePeerDependencies: - '@gql.tada/svelte-support' - '@gql.tada/vue-support' @@ -3968,20 +3967,22 @@ snapshots: dependencies: '@scure/base': 1.2.6 - '@mysten/wallet-standard@0.19.7(typescript@5.9.3)': + '@mysten/wallet-standard@0.19.9(typescript@5.9.3)': dependencies: - '@mysten/sui': 1.45.0(typescript@5.9.3) + '@mysten/sui': 1.45.2(typescript@5.9.3) '@wallet-standard/core': 1.1.1 transitivePeerDependencies: - '@gql.tada/svelte-support' - '@gql.tada/vue-support' - typescript - '@mysten/window-wallet-core@0.1.0': + '@mysten/window-wallet-core@0.1.1(typescript@5.9.3)': dependencies: '@mysten/utils': 0.2.0 - jose: 6.1.2 - valibot: 0.36.0 + jose: 6.1.3 + valibot: 1.2.0(typescript@5.9.3) + transitivePeerDependencies: + - typescript '@nanostores/react@1.0.0(nanostores@1.1.0)(react@19.2.0)': dependencies: @@ -4083,270 +4084,270 @@ snapshots: '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.7)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-context@1.1.2(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-context@1.1.2(@types/react@19.2.7)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.0) aria-hidden: 1.2.6 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) + react-remove-scroll: 2.7.2(@types/react@19.2.7)(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-direction@1.1.1(@types/react@19.2.7)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.7)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-id@1.1.1(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-id@1.1.1(@types/react@19.2.7)(react@19.2.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.0) aria-hidden: 1.2.6 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) + react-remove-scroll: 2.7.2(@types/react@19.2.7)(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.7)(react@19.2.0) '@radix-ui/rect': 1.1.1 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.7)(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-slot@1.2.4(@types/react@19.2.7)(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.7)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.7)(react@19.2.0)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.7)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.7)(react@19.2.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.7)(react@19.2.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.7)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.7)(react@19.2.0)': dependencies: '@radix-ui/rect': 1.1.1 react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.6)(react@19.2.0)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.7)(react@19.2.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 '@radix-ui/rect@1.1.1': {} @@ -4369,80 +4370,80 @@ snapshots: dependencies: tslib: 2.8.1 - '@tailwindcss/node@4.1.17': + '@tailwindcss/node@4.1.18': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.18.4 jiti: 2.6.1 lightningcss: 1.30.2 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.1.17 + tailwindcss: 4.1.18 - '@tailwindcss/oxide-android-arm64@4.1.17': + '@tailwindcss/oxide-android-arm64@4.1.18': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.17': + '@tailwindcss/oxide-darwin-arm64@4.1.18': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.17': + '@tailwindcss/oxide-darwin-x64@4.1.18': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.17': + '@tailwindcss/oxide-freebsd-x64@4.1.18': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.17': + '@tailwindcss/oxide-linux-x64-musl@4.1.18': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.17': + '@tailwindcss/oxide-wasm32-wasi@4.1.18': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': optional: true - '@tailwindcss/oxide@4.1.17': + '@tailwindcss/oxide@4.1.18': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.17 - '@tailwindcss/oxide-darwin-arm64': 4.1.17 - '@tailwindcss/oxide-darwin-x64': 4.1.17 - '@tailwindcss/oxide-freebsd-x64': 4.1.17 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.17 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.17 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.17 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.17 - '@tailwindcss/oxide-linux-x64-musl': 4.1.17 - '@tailwindcss/oxide-wasm32-wasi': 4.1.17 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 - - '@tailwindcss/postcss@4.1.17': + '@tailwindcss/oxide-android-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-x64': 4.1.18 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 + + '@tailwindcss/postcss@4.1.18': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.1.17 - '@tailwindcss/oxide': 4.1.17 + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 postcss: 8.5.6 - tailwindcss: 4.1.17 + tailwindcss: 4.1.18 - '@tanstack/query-core@5.90.10': {} + '@tanstack/query-core@5.90.12': {} - '@tanstack/react-query@5.90.10(react@19.2.0)': + '@tanstack/react-query@5.90.12(react@19.2.0)': dependencies: - '@tanstack/query-core': 5.90.10 + '@tanstack/query-core': 5.90.12 react: 19.2.0 '@tootallnate/once@2.0.0': {} @@ -4462,7 +4463,7 @@ snapshots: '@types/long@4.0.2': {} - '@types/node@20.19.25': + '@types/node@20.19.27': dependencies: undici-types: 6.21.0 @@ -4470,22 +4471,22 @@ snapshots: '@types/prop-types@15.7.15': {} - '@types/react-dom@19.2.3(@types/react@19.2.6)': + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@types/react-transition-group@4.4.12(@types/react@19.2.6)': + '@types/react-transition-group@4.4.12(@types/react@19.2.7)': dependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@types/react@19.2.6': + '@types/react@19.2.7': dependencies: csstype: 3.2.3 '@types/request@2.48.13': dependencies: '@types/caseless': 0.12.5 - '@types/node': 20.19.25 + '@types/node': 20.19.27 '@types/tough-cookie': 4.0.5 form-data: 2.5.5 @@ -4495,16 +4496,15 @@ snapshots: dependencies: uuid: 13.0.0 - '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/type-utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.47.0 - eslint: 9.39.1(jiti@2.6.1) - graphemer: 1.4.0 + '@typescript-eslint/parser': 8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.49.0 + '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.49.0 + eslint: 9.39.2(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.9.3) @@ -4512,80 +4512,79 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.47.0 + '@typescript-eslint/scope-manager': 8.49.0 + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.49.0 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.47.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.49.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) - '@typescript-eslint/types': 8.47.0 + '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) + '@typescript-eslint/types': 8.49.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.47.0': + '@typescript-eslint/scope-manager@8.49.0': dependencies: - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/visitor-keys': 8.47.0 + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/visitor-keys': 8.49.0 - '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.47.0': {} + '@typescript-eslint/types@8.49.0': {} - '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.47.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/visitor-keys': 8.47.0 + '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/visitor-keys': 8.49.0 debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 + tinyglobby: 0.2.15 ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.49.0 + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.47.0': + '@typescript-eslint/visitor-keys@8.49.0': dependencies: - '@typescript-eslint/types': 8.47.0 + '@typescript-eslint/types': 8.49.0 eslint-visitor-keys: 4.2.1 '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -4647,7 +4646,7 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vanilla-extract/css@1.17.4(babel-plugin-macros@3.1.0)': + '@vanilla-extract/css@1.17.5(babel-plugin-macros@3.1.0)': dependencies: '@emotion/hash': 0.9.2 '@vanilla-extract/private': 1.0.9 @@ -4670,9 +4669,9 @@ snapshots: '@vanilla-extract/private@1.0.9': {} - '@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.17.4(babel-plugin-macros@3.1.0))': + '@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.17.5(babel-plugin-macros@3.1.0))': dependencies: - '@vanilla-extract/css': 1.17.4(babel-plugin-macros@3.1.0) + '@vanilla-extract/css': 1.17.5(babel-plugin-macros@3.1.0) '@wallet-standard/app@1.1.0': dependencies: @@ -4779,7 +4778,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 @@ -4789,7 +4788,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -4799,7 +4798,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -4808,21 +4807,21 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-shim-unscopables: 1.1.0 @@ -4831,7 +4830,7 @@ snapshots: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 @@ -4882,7 +4881,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.8.30: {} + baseline-browser-mapping@2.9.7: {} bignumber.js@9.3.1: {} @@ -4899,13 +4898,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.0: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.8.30 - caniuse-lite: 1.0.30001756 - electron-to-chromium: 1.5.259 + baseline-browser-mapping: 2.9.7 + caniuse-lite: 1.0.30001760 + electron-to-chromium: 1.5.267 node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.28.0) + update-browserslist-db: 1.2.2(browserslist@4.28.1) buffer-equal-constant-time@1.0.1: {} @@ -4928,7 +4927,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001756: {} + caniuse-lite@1.0.30001760: {} chalk@4.1.2: dependencies: @@ -5067,7 +5066,7 @@ snapshots: dependencies: safe-buffer: 5.2.1 - electron-to-chromium@1.5.259: {} + electron-to-chromium@1.5.267: {} emoji-regex@8.0.0: {} @@ -5077,7 +5076,7 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.18.3: + enhanced-resolve@5.18.4: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 @@ -5086,7 +5085,7 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract@1.24.0: + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -5147,12 +5146,12 @@ snapshots: es-errors@1.3.0: {} - es-iterator-helpers@1.2.1: + es-iterator-helpers@1.2.2: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 @@ -5191,18 +5190,18 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@16.0.3(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + eslint-config-next@16.0.3(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 16.0.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-hooks: 7.0.1(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1)) globals: 16.4.0 - typescript-eslint: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + typescript-eslint: 8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -5219,33 +5218,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) get-tsconfig: 4.13.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@typescript-eslint/parser': 8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -5254,9 +5253,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -5268,13 +5267,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@2.6.1)): dependencies: aria-query: 5.3.2 array-includes: 3.1.9 @@ -5284,7 +5283,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -5293,26 +5292,26 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@2.6.1)): dependencies: '@babel/core': 7.28.5 '@babel/parser': 7.28.5 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) hermes-parser: 0.25.1 - zod: 4.1.12 - zod-validation-error: 4.0.2(zod@4.1.12) + zod: 4.1.13 + zod-validation-error: 4.0.2(zod@4.1.13) transitivePeerDependencies: - supports-color - eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.6.1)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.39.1(jiti@2.6.1) + es-iterator-helpers: 1.2.2 + eslint: 9.39.2(jiti@2.6.1) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -5335,15 +5334,15 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1(jiti@2.6.1): + eslint@9.39.2(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.1 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 @@ -5410,14 +5409,6 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -5477,7 +5468,7 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 - framer-motion@12.23.24(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + framer-motion@12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: motion-dom: 12.23.23 motion-utils: 12.23.6 @@ -5580,14 +5571,14 @@ snapshots: gaxios: 6.7.1 gcp-metadata: 6.1.1 gtoken: 7.1.0 - jws: 4.0.0 + jws: 4.0.1 transitivePeerDependencies: - encoding - supports-color google-gax@4.6.1: dependencies: - '@grpc/grpc-js': 1.14.1 + '@grpc/grpc-js': 1.14.3 '@grpc/proto-loader': 0.7.15 '@types/long': 4.0.2 abort-controller: 3.0.0 @@ -5610,8 +5601,8 @@ snapshots: gql.tada@1.9.0(graphql@16.12.0)(typescript@5.9.3): dependencies: '@0no-co/graphql.web': 1.2.0(graphql@16.12.0) - '@0no-co/graphqlsp': 1.15.1(graphql@16.12.0)(typescript@5.9.3) - '@gql.tada/cli-utils': 1.7.2(@0no-co/graphqlsp@1.15.1(graphql@16.12.0)(typescript@5.9.3))(graphql@16.12.0)(typescript@5.9.3) + '@0no-co/graphqlsp': 1.15.2(graphql@16.12.0)(typescript@5.9.3) + '@gql.tada/cli-utils': 1.7.2(@0no-co/graphqlsp@1.15.2(graphql@16.12.0)(typescript@5.9.3))(graphql@16.12.0)(typescript@5.9.3) '@gql.tada/internal': 1.0.8(graphql@16.12.0)(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -5621,14 +5612,12 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - graphql@16.12.0: {} gtoken@7.1.0: dependencies: gaxios: 6.7.1 - jws: 4.0.0 + jws: 4.0.1 transitivePeerDependencies: - encoding - supports-color @@ -5841,7 +5830,7 @@ snapshots: jiti@2.6.1: {} - jose@6.1.2: {} + jose@6.1.3: {} js-tokens@4.0.0: {} @@ -5882,7 +5871,7 @@ snapshots: ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - jws@4.0.0: + jws@4.0.1: dependencies: jwa: 2.0.1 safe-buffer: 5.2.1 @@ -6036,7 +6025,7 @@ snapshots: dependencies: '@next/env': 16.0.3 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001756 + caniuse-lite: 1.0.30001760 postcss: 8.4.31 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) @@ -6090,14 +6079,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 object.values@1.2.1: dependencies: @@ -6198,7 +6187,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.19.25 + '@types/node': 20.19.27 long: 5.3.2 proxy-from-env@1.1.0: {} @@ -6214,34 +6203,34 @@ snapshots: react-is@16.13.1: {} - react-is@19.2.0: {} + react-is@19.2.3: {} - react-remove-scroll-bar@2.3.8(@types/react@19.2.6)(react@19.2.0): + react-remove-scroll-bar@2.3.8(@types/react@19.2.7)(react@19.2.0): dependencies: react: 19.2.0 - react-style-singleton: 2.2.3(@types/react@19.2.6)(react@19.2.0) + react-style-singleton: 2.2.3(@types/react@19.2.7)(react@19.2.0) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - react-remove-scroll@2.7.1(@types/react@19.2.6)(react@19.2.0): + react-remove-scroll@2.7.2(@types/react@19.2.7)(react@19.2.0): dependencies: react: 19.2.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.6)(react@19.2.0) - react-style-singleton: 2.2.3(@types/react@19.2.6)(react@19.2.0) + react-remove-scroll-bar: 2.3.8(@types/react@19.2.7)(react@19.2.0) + react-style-singleton: 2.2.3(@types/react@19.2.7)(react@19.2.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.6)(react@19.2.0) - use-sidecar: 1.1.3(@types/react@19.2.6)(react@19.2.0) + use-callback-ref: 1.3.3(@types/react@19.2.7)(react@19.2.0) + use-sidecar: 1.1.3(@types/react@19.2.7)(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - react-style-singleton@2.2.3(@types/react@19.2.6)(react@19.2.0): + react-style-singleton@2.2.3(@types/react@19.2.7)(react@19.2.0): dependencies: get-nonce: 1.0.1 react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: @@ -6264,7 +6253,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -6459,14 +6448,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -6480,7 +6469,7 @@ snapshots: string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.trim@1.2.10: dependencies: @@ -6488,7 +6477,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -6537,7 +6526,7 @@ snapshots: tailwind-merge@3.4.0: {} - tailwindcss@4.1.17: {} + tailwindcss@4.1.18: {} tapable@2.3.0: {} @@ -6613,13 +6602,13 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -6659,9 +6648,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.1.4(browserslist@4.28.0): + update-browserslist-db@1.2.2(browserslist@4.28.1): dependencies: - browserslist: 4.28.0 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -6669,20 +6658,20 @@ snapshots: dependencies: punycode: 2.3.1 - use-callback-ref@1.3.3(@types/react@19.2.6)(react@19.2.0): + use-callback-ref@1.3.3(@types/react@19.2.7)(react@19.2.0): dependencies: react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - use-sidecar@1.1.3(@types/react@19.2.6)(react@19.2.0): + use-sidecar@1.1.3(@types/react@19.2.7)(react@19.2.0): dependencies: detect-node-es: 1.1.0 react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 use-sync-external-store@1.6.0(react@19.2.0): dependencies: @@ -6696,7 +6685,9 @@ snapshots: uuid@9.0.1: {} - valibot@0.36.0: {} + valibot@1.2.0(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 webidl-conversions@3.0.1: {} @@ -6780,15 +6771,15 @@ snapshots: yocto-queue@0.1.0: {} - zod-validation-error@4.0.2(zod@4.1.12): + zod-validation-error@4.0.2(zod@4.1.13): dependencies: - zod: 4.1.12 + zod: 4.1.13 - zod@4.1.12: {} + zod@4.1.13: {} - zustand@4.5.7(@types/react@19.2.6)(react@19.2.0): + zustand@4.5.7(@types/react@19.2.7)(react@19.2.0): dependencies: use-sync-external-store: 1.6.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 react: 19.2.0 diff --git a/frontend/src/app/app/sections/BuilderSection/components/BlockCard.tsx b/frontend/src/app/app/sections/BuilderSection/components/BlockCard.tsx index af4f182..f30d8c5 100644 --- a/frontend/src/app/app/sections/BuilderSection/components/BlockCard.tsx +++ b/frontend/src/app/app/sections/BuilderSection/components/BlockCard.tsx @@ -15,6 +15,7 @@ interface BlockCardProps { onRemove: (id: string) => void; onUpdateParam: (id: string, key: string, value: any) => void; isLast: boolean; + network?: string; } const getBlockColor = (type: string) => { @@ -39,7 +40,8 @@ export function BlockCard({ tokenMap, onRemove, onUpdateParam, - isLast + isLast, + network = "mainnet" }: BlockCardProps) { const colors = getBlockColor(block.type); @@ -156,6 +158,48 @@ export function BlockCard({ placeholder="0.0" /> + {/* Turbos-specific fields (only on testnet) */} + {network === "testnet" && ( + <> +
+ + onUpdateParam(block.id, "pool_id", e.target.value)} + className="w-full px-3 py-2 bg-black border border-gray-700 focus:border-gray-500 font-mono text-sm text-white outline-none transition-colors" + placeholder="0x..." + /> +

Required for Turbos on testnet

+
+
+ + onUpdateParam(block.id, "coin_type_a", e.target.value)} + className="w-full px-3 py-2 bg-black border border-gray-700 focus:border-gray-500 font-mono text-sm text-white outline-none transition-colors" + placeholder="0x2::sui::SUI" + /> +
+
+ + onUpdateParam(block.id, "coin_type_b", e.target.value)} + className="w-full px-3 py-2 bg-black border border-gray-700 focus:border-gray-500 font-mono text-sm text-white outline-none transition-colors" + placeholder="0x...::usdc::USDC" + /> +
+ + )} )} diff --git a/frontend/src/app/app/sections/BuilderSection/components/Canvas.tsx b/frontend/src/app/app/sections/BuilderSection/components/Canvas.tsx index 3ce0a03..7bd1ebd 100644 --- a/frontend/src/app/app/sections/BuilderSection/components/Canvas.tsx +++ b/frontend/src/app/app/sections/BuilderSection/components/Canvas.tsx @@ -10,9 +10,10 @@ interface CanvasProps { tokenMap: Record; onRemoveBlock: (id: string) => void; onUpdateBlockParam: (id: string, key: string, value: any) => void; + network?: string; } -export function Canvas({ blocks, tokenMap, onRemoveBlock, onUpdateBlockParam }: CanvasProps) { +export function Canvas({ blocks, tokenMap, onRemoveBlock, onUpdateBlockParam, network = "mainnet" }: CanvasProps) { return (
{/* Grid Pattern Background */} @@ -81,6 +82,7 @@ export function Canvas({ blocks, tokenMap, onRemoveBlock, onUpdateBlockParam }: onRemove={onRemoveBlock} onUpdateParam={onUpdateBlockParam} isLast={index === blocks.length - 1} + network={network} /> ); })} diff --git a/frontend/src/app/app/sections/BuilderSection/hooks/useSimulation.ts b/frontend/src/app/app/sections/BuilderSection/hooks/useSimulation.ts index bf7aa08..df6e59f 100644 --- a/frontend/src/app/app/sections/BuilderSection/hooks/useSimulation.ts +++ b/frontend/src/app/app/sections/BuilderSection/hooks/useSimulation.ts @@ -11,9 +11,10 @@ interface UseSimulationProps { onSuccess: (result: SimulationResult) => void; builderMode?: 'blocks' | 'json'; rawJson?: string; + network?: string; } -export function useSimulation({ blocks, tokenMap, senderAddress, onSuccess, builderMode = 'blocks', rawJson = '' }: UseSimulationProps) { +export function useSimulation({ blocks, tokenMap, senderAddress, onSuccess, builderMode = 'blocks', rawJson = '', network = 'mainnet' }: UseSimulationProps) { const [isSimulating, setIsSimulating] = useState(false); const [error, setError] = useState(null); @@ -42,7 +43,7 @@ export function useSimulation({ blocks, tokenMap, senderAddress, onSuccess, buil throw new Error("Invalid JSON"); } } else { - strategy = buildStrategyFromBlocks(blocks, tokenMap, senderAddress); + strategy = buildStrategyFromBlocks(blocks, tokenMap, senderAddress, network as "mainnet" | "testnet" | "devnet"); } // Debug: log the generated strategy diff --git a/frontend/src/app/app/sections/BuilderSection/index.tsx b/frontend/src/app/app/sections/BuilderSection/index.tsx index eae7001..d1af4f5 100644 --- a/frontend/src/app/app/sections/BuilderSection/index.tsx +++ b/frontend/src/app/app/sections/BuilderSection/index.tsx @@ -1,7 +1,7 @@ "use client"; import { Box, Grid } from "@mui/material"; -import { useCurrentAccount } from "@mysten/dapp-kit"; +import { useCurrentAccount, useSuiClientContext } from "@mysten/dapp-kit"; import { useTokens } from "./hooks/useTokens"; import { useBlocks } from "./hooks/useBlocks"; import { useSimulation } from "./hooks/useSimulation"; @@ -25,6 +25,7 @@ interface BuilderSectionProps { export function BuilderSection({ onNavigate }: BuilderSectionProps) { const currentAccount = useCurrentAccount(); + const { network } = useSuiClientContext(); const tokenMap = useTokens(); const [isSaveModalOpen, setIsSaveModalOpen] = useState(false); const { uploadWorkflow } = useWorkflowActions(); @@ -57,6 +58,7 @@ export function BuilderSection({ onNavigate }: BuilderSectionProps) { onSuccess: setSimulationResult, builderMode, rawJson, + network: network || "mainnet", }); const handleClear = () => { @@ -84,7 +86,7 @@ export function BuilderSection({ onNavigate }: BuilderSectionProps) { throw new Error("Invalid JSON"); } } else { - strategy = buildStrategyFromBlocks(blocks, tokenMap, currentAccount?.address || "Anonymous"); + strategy = buildStrategyFromBlocks(blocks, tokenMap, currentAccount?.address || "Anonymous", network || "mainnet"); } // Update metadata with user input @@ -269,6 +271,7 @@ export function BuilderSection({ onNavigate }: BuilderSectionProps) { tokenMap={tokenMap} onRemoveBlock={removeBlock} onUpdateBlockParam={updateBlockParam} + network={network || "mainnet"} /> ) : ( diff --git a/frontend/src/app/app/sections/BuilderSection/utils/strategyBuilder.ts b/frontend/src/app/app/sections/BuilderSection/utils/strategyBuilder.ts index 396e86d..b7b06c2 100644 --- a/frontend/src/app/app/sections/BuilderSection/utils/strategyBuilder.ts +++ b/frontend/src/app/app/sections/BuilderSection/utils/strategyBuilder.ts @@ -21,7 +21,7 @@ export function normalizeAmount(amount: string): string { return Math.floor(amountInSui * 1_000_000_000).toString(); } -export function buildStrategyFromBlocks(blocks: Block[], tokenMap: Record, authorAddress: string) { +export function buildStrategyFromBlocks(blocks: Block[], tokenMap: Record, authorAddress: string, network: "mainnet" | "testnet" | "devnet" = "mainnet") { const nodes: any[] = []; const edges: any[] = []; @@ -55,11 +55,25 @@ export function buildStrategyFromBlocks(blocks: Block[], tokenMap: Record`, output_type: "COIN" }, { id: "receipt", type: "FlashLoanReceipt", output_type: "RECEIPT" } @@ -196,12 +210,38 @@ export function buildStrategyFromBlocks(blocks: Block[], tokenMap: Record n.id === flashLoanReceiptNodeId); + if (borrowNode && borrowNode.type === "FLASH_BORROW") { + repayParams.pool_id = borrowNode.params.pool_id || ""; + repayParams.coin_type_a = borrowNode.params.coin_type_a || asset; + repayParams.coin_type_b = borrowNode.params.coin_type_b || ""; + } else { + // Fallback: try to find any FLASH_BORROW node + const anyBorrowNode = nodes.find(n => n.type === "FLASH_BORROW"); + if (anyBorrowNode) { + repayParams.pool_id = anyBorrowNode.params.pool_id || ""; + repayParams.coin_type_a = anyBorrowNode.params.coin_type_a || asset; + repayParams.coin_type_b = anyBorrowNode.params.coin_type_b || ""; + } else { + repayParams.pool_id = ""; + repayParams.coin_type_a = asset; + repayParams.coin_type_b = ""; + } + } + } + nodes.push({ id: nodeId, type: "FLASH_REPAY", - protocol: "NAVI", - params: { asset }, + protocol: repayProtocol, + params: repayParams, inputs: { coin_repay: `${mergeNodeId}.merged_coin`, receipt: flashLoanReceiptNodeId ? `${flashLoanReceiptNodeId}.${flashLoanReceiptId}` : "receipt" diff --git a/frontend/src/hooks/useUserData.ts b/frontend/src/hooks/useUserData.ts new file mode 100644 index 0000000..8de8286 --- /dev/null +++ b/frontend/src/hooks/useUserData.ts @@ -0,0 +1,432 @@ +import { useState, useCallback, useEffect } from 'react'; +import { useCurrentAccount, useSignTransaction, useSuiClient } from '@mysten/dapp-kit'; +import { Transaction } from '@mysten/sui/transactions'; + +const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; + +interface SavedStrategy { + id: number; + name: string; + description: string; + strategyJson: any; + createdAt: number; + updatedAt: number; +} + +interface ExecutionEntry { + id: number; + workflowName: string; + workflowId: string; + status: string; + txDigest: string; + timestamp: number; + gasUsed: number; + resultData: any; +} + +/** + * Hook to interact with the user_data smart contract + * Replaces localStorage usage for decentralized data storage + */ +export function useUserData() { + const account = useCurrentAccount(); + const { mutateAsync: signTransaction } = useSignTransaction(); + const suiClient = useSuiClient(); + + const [storageObjectId, setStorageObjectId] = useState(null); + const [isLoadingStorage, setIsLoadingStorage] = useState(false); + const [strategies, setStrategies] = useState([]); + const [executionHistory, setExecutionHistory] = useState([]); + + /** + * Check if user has a storage object and get its ID + */ + const checkUserStorage = useCallback(async () => { + if (!account?.address) return null; + + setIsLoadingStorage(true); + try { + const response = await fetch(`${API_URL}/api/userdata/storage/${account.address}`); + const data = await response.json(); + + if (data.exists && data.storageObjectId) { + setStorageObjectId(data.storageObjectId); + return data.storageObjectId; + } + return null; + } catch (error) { + console.error('Error checking user storage:', error); + return null; + } finally { + setIsLoadingStorage(false); + } + }, [account?.address]); + + /** + * Create a new storage object for the user + */ + const createStorage = useCallback(async () => { + if (!account?.address) { + throw new Error('No wallet connected'); + } + + try { + // 1. Build transaction + const response = await fetch(`${API_URL}/api/userdata/create-storage`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ userAddress: account.address }), + }); + + if (!response.ok) { + throw new Error('Failed to build transaction'); + } + + const { transactionBytes } = await response.json(); + const txBytes = new Uint8Array(transactionBytes); + + // 2. Sign transaction + const transaction = Transaction.from(txBytes); + const signedTx = await signTransaction({ transaction }); + + // 3. Execute transaction + const result = await suiClient.executeTransactionBlock({ + transactionBlock: signedTx.bytes, + signature: signedTx.signature, + options: { + showEffects: true, + showObjectChanges: true, + }, + }); + + // 4. Extract storage object ID from created objects + const createdObjects = result.objectChanges?.filter( + (change: any) => change.type === 'created' + ); + const storageObject = createdObjects?.find((obj: any) => + obj.objectType?.includes('::user_data::UserDataStorage') + ); + + if (storageObject && 'objectId' in storageObject) { + setStorageObjectId(storageObject.objectId); + return storageObject.objectId; + } + + throw new Error('Failed to create storage object'); + } catch (error) { + console.error('Error creating storage:', error); + throw error; + } + }, [account?.address, signTransaction, suiClient]); + + /** + * Ensure storage exists, create if not + */ + const ensureStorage = useCallback(async () => { + let objectId = storageObjectId; + + if (!objectId) { + objectId = await checkUserStorage(); + } + + if (!objectId) { + objectId = await createStorage(); + } + + return objectId; + }, [storageObjectId, checkUserStorage, createStorage]); + + /** + * Save a new strategy + */ + const saveStrategy = useCallback( + async (name: string, description: string, strategyJson: any) => { + if (!account?.address) { + throw new Error('No wallet connected'); + } + + const objectId = await ensureStorage(); + + try { + // 1. Build transaction + const response = await fetch(`${API_URL}/api/userdata/save-strategy`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + userAddress: account.address, + storageObjectId: objectId, + name, + description, + strategyJson, + }), + }); + + if (!response.ok) { + throw new Error('Failed to build transaction'); + } + + const { transactionBytes } = await response.json(); + const txBytes = new Uint8Array(transactionBytes); + + // 2. Sign transaction + const transaction = Transaction.from(txBytes); + const signedTx = await signTransaction({ transaction }); + + // 3. Execute transaction + const result = await suiClient.executeTransactionBlock({ + transactionBlock: signedTx.bytes, + signature: signedTx.signature, + options: { + showEffects: true, + showEvents: true, + }, + }); + + // Refresh strategies list + await loadStrategies(); + + return result.digest; + } catch (error) { + console.error('Error saving strategy:', error); + throw error; + } + }, + [account?.address, ensureStorage, signTransaction, suiClient] + ); + + /** + * Delete a strategy + */ + const deleteStrategy = useCallback( + async (strategyId: number) => { + if (!account?.address || !storageObjectId) { + throw new Error('No wallet connected or storage not initialized'); + } + + try { + // 1. Build transaction + const response = await fetch(`${API_URL}/api/userdata/delete-strategy`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + userAddress: account.address, + storageObjectId, + strategyId, + }), + }); + + if (!response.ok) { + throw new Error('Failed to build transaction'); + } + + const { transactionBytes } = await response.json(); + const txBytes = new Uint8Array(transactionBytes); + + // 2. Sign transaction + const transaction = Transaction.from(txBytes); + const signedTx = await signTransaction({ transaction }); + + // 3. Execute transaction + const result = await suiClient.executeTransactionBlock({ + transactionBlock: signedTx.bytes, + signature: signedTx.signature, + options: { + showEffects: true, + }, + }); + + // Refresh strategies list + await loadStrategies(); + + return result.digest; + } catch (error) { + console.error('Error deleting strategy:', error); + throw error; + } + }, + [account?.address, storageObjectId, signTransaction, suiClient] + ); + + /** + * Record a workflow execution + */ + const recordExecution = useCallback( + async ( + workflowName: string, + workflowId: string, + status: string, + txDigest: string, + gasUsed: number, + resultData: any + ) => { + if (!account?.address) { + throw new Error('No wallet connected'); + } + + const objectId = await ensureStorage(); + + try { + // 1. Build transaction + const response = await fetch(`${API_URL}/api/userdata/record-execution`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + userAddress: account.address, + storageObjectId: objectId, + workflowName, + workflowId, + status, + txDigest, + gasUsed, + resultData, + }), + }); + + if (!response.ok) { + throw new Error('Failed to build transaction'); + } + + const { transactionBytes } = await response.json(); + const txBytes = new Uint8Array(transactionBytes); + + // 2. Sign transaction + const transaction = Transaction.from(txBytes); + const signedTx = await signTransaction({ transaction }); + + // 3. Execute transaction + const result = await suiClient.executeTransactionBlock({ + transactionBlock: signedTx.bytes, + signature: signedTx.signature, + options: { + showEffects: true, + }, + }); + + // Refresh history + await loadExecutionHistory(); + + return result.digest; + } catch (error) { + console.error('Error recording execution:', error); + throw error; + } + }, + [account?.address, ensureStorage, signTransaction, suiClient] + ); + + /** + * Clear execution history + */ + const clearExecutionHistory = useCallback(async () => { + if (!account?.address || !storageObjectId) { + throw new Error('No wallet connected or storage not initialized'); + } + + try { + // 1. Build transaction + const response = await fetch(`${API_URL}/api/userdata/clear-history`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + userAddress: account.address, + storageObjectId, + }), + }); + + if (!response.ok) { + throw new Error('Failed to build transaction'); + } + + const { transactionBytes } = await response.json(); + const txBytes = new Uint8Array(transactionBytes); + + // 2. Sign transaction + const transaction = Transaction.from(txBytes); + const signedTx = await signTransaction({ transaction }); + + // 3. Execute transaction + const result = await suiClient.executeTransactionBlock({ + transactionBlock: signedTx.bytes, + signature: signedTx.signature, + options: { + showEffects: true, + }, + }); + + // Clear local state + setExecutionHistory([]); + + return result.digest; + } catch (error) { + console.error('Error clearing history:', error); + throw error; + } + }, [account?.address, storageObjectId, signTransaction, suiClient]); + + /** + * Load all strategies from smart contract + */ + const loadStrategies = useCallback(async () => { + if (!storageObjectId) return; + + try { + const response = await fetch(`${API_URL}/api/userdata/strategies/${storageObjectId}`); + const data = await response.json(); + setStrategies(data.strategies || []); + } catch (error) { + console.error('Error loading strategies:', error); + } + }, [storageObjectId]); + + /** + * Load execution history from smart contract + */ + const loadExecutionHistory = useCallback(async () => { + if (!storageObjectId) return; + + try { + const response = await fetch(`${API_URL}/api/userdata/history/${storageObjectId}`); + const data = await response.json(); + setExecutionHistory(data.history || []); + } catch (error) { + console.error('Error loading execution history:', error); + } + }, [storageObjectId]); + + // Auto-check storage on mount and account change + useEffect(() => { + if (account?.address) { + checkUserStorage(); + } else { + setStorageObjectId(null); + setStrategies([]); + setExecutionHistory([]); + } + }, [account?.address, checkUserStorage]); + + // Auto-load data when storage is available + useEffect(() => { + if (storageObjectId) { + loadStrategies(); + loadExecutionHistory(); + } + }, [storageObjectId, loadStrategies, loadExecutionHistory]); + + return { + // State + storageObjectId, + isLoadingStorage, + strategies, + executionHistory, + + // Actions + createStorage, + ensureStorage, + saveStrategy, + deleteStrategy, + recordExecution, + clearExecutionHistory, + loadStrategies, + loadExecutionHistory, + }; +}