From 6e0ce870243f144a4a70fc1bf9d03593091e99da Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Thu, 5 Feb 2026 01:45:36 +0100 Subject: [PATCH 1/3] feat: dashboard, monorepo --- .cursor/worktrees.json | 3 - .gitignore | 3 + .husky/commit-msg | 1 + biome.json | 3 +- commitlint.config.cjs | 3 + components.json | 21 - index.html | 2 +- package.json | 88 +- packages/common/package.json | 108 + .../scripts}/generate-client-factory.ts | 2 +- .../generate-tradingview-symbols/index.ts | 7 +- .../generate-tradingview-symbols/utils.ts | 0 .../common/src}/assets/hyperliquid.png | Bin .../src}/assets/tradingview-symbols.json | 6 +- .../common/src}/atoms/actions-atoms.ts | 28 +- packages/common/src/atoms/config-atom.ts | 4 + packages/common/src/atoms/index.ts | 10 + .../common/src}/atoms/markets-atoms.ts | 30 +- .../src}/atoms/orders-pending-actions-atom.ts | 10 +- .../common/src}/atoms/portfolio-atoms.ts | 10 +- .../atoms/position-pending-actions-atom.ts | 14 +- .../common/src}/atoms/providers-atoms.ts | 30 +- .../common/src}/atoms/tokens-atoms.ts | 10 +- {src => packages/common/src}/atoms/utils.ts | 0 .../common/src}/atoms/wallet-atom.ts | 6 +- packages/common/src/components/index.ts | 24 + .../src/components/molecules}/Chart/index.tsx | 162 +- .../src/components/molecules}/Chart/state.ts | 8 +- .../components/molecules/address-switcher.tsx | 63 +- .../src}/components/molecules/forms/index.tsx | 17 +- .../common/src/components/molecules/index.ts | 7 + .../components/molecules}/leverage-dialog.tsx | 95 +- .../molecules}/limit-price-dialog.tsx | 26 +- .../molecules}/order-type-dialog.tsx | 18 +- .../molecules/percentage-slider.tsx | 7 +- .../molecules/sign-transactions/index.tsx | 143 +- .../components/molecules/toggle-group.tsx | 2 +- .../src}/components/molecules/token-icon.tsx | 5 +- .../components/molecules/tp-sl-dialog.tsx | 222 +- .../common/src}/components/ui/button.tsx | 13 +- .../common/src}/components/ui/card.tsx | 7 +- .../common/src}/components/ui/dialog.tsx | 6 +- packages/common/src/components/ui/divider.tsx | 3 + packages/common/src/components/ui/popover.tsx | 128 + packages/common/src/components/ui/select.tsx | 213 + .../common/src}/components/ui/skeleton.tsx | 2 +- .../common/src}/components/ui/slider.tsx | 2 +- .../common/src}/components/ui/spinner.tsx | 2 +- .../common/src}/components/ui/tabs.tsx | 7 +- packages/common/src/components/ui/text.tsx | 100 + .../common/src}/components/ui/toaster.tsx | 0 .../common/src}/context/appkit.tsx | 4 +- .../common/src}/context/index.tsx | 8 +- .../common/src}/context/root-container.tsx | 0 .../common/src}/domain/chains/evm.ts | 0 packages/common/src/domain/chains/index.ts | 6 + .../common/src}/domain/chains/ledger.ts | 2 +- packages/common/src/domain/index.ts | 6 + {src => packages/common/src}/domain/signer.ts | 2 +- {src => packages/common/src}/domain/tokens.ts | 2 +- .../common/src}/domain/transactions.ts | 1 + {src => packages/common/src}/domain/types.ts | 2 +- {src => packages/common/src}/domain/wallet.ts | 14 +- packages/common/src/hooks/index.ts | 3 + .../common/src}/hooks/use-order-actions.ts | 2 +- .../common/src}/hooks/use-position-actions.ts | 3 +- .../common/src}/hooks/use-tp-sl-orders.ts | 2 +- packages/common/src/lib/formatting.ts | 163 + packages/common/src/lib/index.ts | 3 + packages/common/src/lib/math.ts | 486 +++ packages/common/src/lib/utils.ts | 40 + .../src}/services/api-client/api-schemas.ts | 169 +- .../services/api-client/client-factory.ts | 141 +- .../common/src}/services/api-client/index.ts | 4 +- .../common/src}/services/config.ts | 1 - .../common/src}/services/constants.ts | 0 .../common/src/services/http-client/index.ts | 0 packages/common/src/services/index.ts | 12 + .../common/src}/services/runtime.ts | 14 +- .../src}/services/wallet/browser-signer.ts | 10 +- .../services/wallet/ledger-signer/index.ts | 12 +- .../services/wallet/ledger-signer/utils.ts | 12 +- .../common/src}/services/wallet/signer.ts | 2 +- .../src}/services/wallet/wallet-service.ts | 12 +- packages/common/src/styles/base.css | 24 + packages/common/src/styles/index.css | 6 + packages/common/src/styles/theme.css | 97 + .../common/src/vite.config.ts | 41 +- .../components/leverage-dialog.test.tsx | 2 +- .../components/limit-price-dialog.test.tsx | 2 +- .../components/order-type-dialog.test.tsx | 2 +- .../tests}/components/tp-sl-dialog.test.tsx | 4 +- .../common/tests}/components/wrapper.tsx | 6 +- packages/common/tsconfig.json | 10 + packages/common/vite.config.ts | 12 + packages/dashboard/.env.example | 12 + packages/dashboard/index.html | 19 + packages/dashboard/package.json | 66 + .../public}/apple-touch-icon-180x180.png | Bin .../dashboard/public}/favicon.ico | Bin .../dashboard/public}/logo-192x192.png | Bin .../dashboard/public}/logo-512x512.png | Bin .../dashboard/public}/logo-64x64.png | Bin .../public}/maskable-icon-512x512.png | Bin .../dashboard/public}/yield_xyz.png | Bin packages/dashboard/src/app.tsx | 44 + .../src/atoms/selected-market-atom.ts | 22 + .../dashboard/src/components/atoms/logo.tsx | 28 + .../src/components/modules/root/Preload.tsx | 45 + .../src/components/modules/trade/chart.tsx | 59 + .../src/components/modules/trade/index.tsx | 20 + .../trade/market-info/bar-skeleton.tsx | 60 + .../modules/trade/market-info/index.tsx | 174 + .../market-info/market-selector-popover.tsx | 248 ++ .../modules/trade/order-form/index.tsx | 542 +++ .../modules/trade/order-form/sign-dialog.tsx | 86 + .../modules/trade/order-form/state.tsx | 419 ++ .../header/deposit/deposit-dialog.tsx | 360 ++ .../molecules/header/deposit/state.tsx | 381 ++ .../src/components/molecules/header/index.tsx | 230 + .../molecules/header/withdraw/state.tsx | 269 ++ .../header/withdraw/withdraw-dialog.tsx | 224 + .../positions/close-position-dialog.tsx | 179 + .../components/molecules/positions/index.tsx | 70 + .../molecules/positions/orders-tab.tsx | 196 + .../molecules/positions/positions-tab.tsx | 386 ++ .../components/molecules/positions/shared.tsx | 136 + .../components/molecules/positions/state.ts | 178 + packages/dashboard/src/main.css | 11 + {src => packages/dashboard/src}/main.tsx | 6 +- packages/dashboard/src/routeTree.gen.ts | 59 + packages/dashboard/src/routes/__root.tsx | 17 + packages/dashboard/src/routes/index.tsx | 6 + packages/dashboard/src/styles.css | 17 + packages/dashboard/tsconfig.json | 12 + packages/dashboard/vite.config.ts | 4 + packages/widget/.env.example | 12 + packages/widget/index.html | 19 + packages/widget/package.json | 66 + .../public/apple-touch-icon-180x180.png | Bin 0 -> 3095 bytes packages/widget/public/favicon.ico | Bin 0 -> 1609 bytes packages/widget/public/logo-192x192.png | Bin 0 -> 6071 bytes packages/widget/public/logo-512x512.png | Bin 0 -> 24204 bytes packages/widget/public/logo-64x64.png | Bin 0 -> 1956 bytes .../widget/public/maskable-icon-512x512.png | Bin 0 -> 12755 bytes packages/widget/public/yield_xyz.png | Bin 0 -> 235471 bytes {src => packages/widget/src}/app.tsx | 22 +- .../modules/Account/Deposit/index.tsx | 55 +- .../modules/Account/Deposit/sign.tsx | 2 +- .../modules/Account/Deposit/state.tsx | 80 +- .../modules/Account/Withdraw/index.tsx | 50 +- .../modules/Account/Withdraw/sign.tsx | 2 +- .../modules/Account/Withdraw/state.tsx | 53 +- .../components/modules/Account/balance.tsx | 36 +- .../modules/Home/AssetList/index.tsx | 36 +- .../modules/Home/AssetList/item.tsx | 45 +- .../modules/Home/Positions/index.tsx | 85 +- .../modules/Home/Positions/order-card.tsx | 65 +- .../modules/Home/Positions/position-card.tsx | 130 +- .../src}/components/modules/Home/index.tsx | 65 +- .../modules/Order/Overview/index.tsx | 141 +- .../modules/Order/Overview/loading.tsx | 4 +- .../modules/Order/Overview/state.tsx | 93 +- .../src}/components/modules/Order/sign.tsx | 2 +- .../modules/PositionDetails/Close/index.tsx | 90 +- .../modules/PositionDetails/Close/sign.tsx | 2 +- .../modules/PositionDetails/Close/state.tsx | 27 +- .../Overview/CancelOrder/sign.tsx | 2 +- .../Overview/EditLeverage/sign.tsx | 2 +- .../PositionDetails/Overview/Orders/index.tsx | 105 +- .../Overview/Position/index.tsx | 168 +- .../Overview/Position/sign.tsx | 2 +- .../Overview/Position/state.ts | 34 +- .../PositionDetails/Overview/index.tsx | 105 +- .../PositionDetails/Overview/loading.tsx | 2 +- .../Overview/modify-dialog.tsx | 18 +- .../Overview/overview-tab-content.tsx | 96 + .../components/modules/Root/PreloadAtoms.tsx | 16 +- .../molecules/account-value-display.tsx | 16 +- .../molecules/navigation/back-button.tsx | 2 +- .../navigation/wallet-protected-route.tsx | 7 +- .../components/molecules/provider-select.tsx | 54 +- .../src}/components/molecules/sign/index.tsx | 16 +- .../molecules/sign/sign-content.tsx | 57 + .../molecules/token-balances-select.tsx | 66 +- packages/widget/src/main.css | 19 + packages/widget/src/main.tsx | 11 + {src => packages/widget/src}/routeTree.gen.ts | 0 .../widget/src}/routes/__root.tsx | 0 .../widget/src}/routes/account/deposit.tsx | 2 +- .../src}/routes/account/deposit_/sign.tsx | 2 +- .../widget/src}/routes/account/index.tsx | 2 +- .../widget/src}/routes/account/withdraw.tsx | 2 +- .../src}/routes/account/withdraw_/sign.tsx | 2 +- {src => packages/widget/src}/routes/index.tsx | 2 +- .../order/$marketId/$side/increase_/index.tsx | 2 +- .../routes/order/$marketId/$side/index.tsx | 2 +- .../routes/order/$marketId/$side/sign.tsx | 2 +- .../$marketId/cancel-order_/sign.tsx | 2 +- .../position-details/$marketId/close.tsx | 2 +- .../$marketId/close_/sign.tsx | 2 +- .../$marketId/edit-leverage_/index.tsx | 2 +- .../$marketId/edit-sl-tp_/sign.tsx | 2 +- .../position-details/$marketId/index.tsx | 2 +- packages/widget/src/styles.css | 2 + packages/widget/tsconfig.json | 12 + packages/widget/vite.config.ts | 4 + pnpm-lock.yaml | 3784 +++++++++++------ pnpm-workspace.yaml | 62 +- src/atoms/config-atom.ts | 11 - .../modules/Order/Overview/utils.ts | 25 - .../Overview/overview-tab-content.tsx | 101 - src/components/ui/divider.tsx | 3 - src/domain/chains/index.ts | 3 - src/domain/market.ts | 8 - src/domain/position.ts | 98 - src/lib/utils.ts | 126 - src/main.css | 13 - src/styles.css | 170 - tsconfig.base.json | 42 + tsconfig.json | 37 +- turbo.json | 23 + 222 files changed, 10638 insertions(+), 3506 deletions(-) delete mode 100644 .cursor/worktrees.json create mode 100755 .husky/commit-msg create mode 100644 commitlint.config.cjs delete mode 100644 components.json create mode 100644 packages/common/package.json rename {scripts => packages/common/scripts}/generate-client-factory.ts (97%) rename {scripts => packages/common/scripts}/generate-tradingview-symbols/index.ts (97%) rename {scripts => packages/common/scripts}/generate-tradingview-symbols/utils.ts (100%) rename {src => packages/common/src}/assets/hyperliquid.png (100%) rename {src => packages/common/src}/assets/tradingview-symbols.json (99%) rename {src => packages/common/src}/atoms/actions-atoms.ts (62%) create mode 100644 packages/common/src/atoms/config-atom.ts create mode 100644 packages/common/src/atoms/index.ts rename {src => packages/common/src}/atoms/markets-atoms.ts (77%) rename {src => packages/common/src}/atoms/orders-pending-actions-atom.ts (77%) rename {src => packages/common/src}/atoms/portfolio-atoms.ts (90%) rename {src => packages/common/src}/atoms/position-pending-actions-atom.ts (86%) rename {src => packages/common/src}/atoms/providers-atoms.ts (50%) rename {src => packages/common/src}/atoms/tokens-atoms.ts (94%) rename {src => packages/common/src}/atoms/utils.ts (100%) rename {src => packages/common/src}/atoms/wallet-atom.ts (88%) create mode 100644 packages/common/src/components/index.ts rename {src/components/modules/PositionDetails/Overview => packages/common/src/components/molecules}/Chart/index.tsx (51%) rename {src/components/modules/PositionDetails/Overview => packages/common/src/components/molecules}/Chart/state.ts (77%) rename {src => packages/common/src}/components/molecules/address-switcher.tsx (85%) rename {src => packages/common/src}/components/molecules/forms/index.tsx (82%) create mode 100644 packages/common/src/components/molecules/index.ts rename {src/components/modules/Order/Overview => packages/common/src/components/molecules}/leverage-dialog.tsx (70%) rename {src/components/modules/Order/Overview => packages/common/src/components/molecules}/limit-price-dialog.tsx (88%) rename {src/components/modules/Order/Overview => packages/common/src/components/molecules}/order-type-dialog.tsx (84%) rename {src => packages/common/src}/components/molecules/percentage-slider.tsx (88%) rename src/components/molecules/sign/sign-content.tsx => packages/common/src/components/molecules/sign-transactions/index.tsx (74%) rename {src => packages/common/src}/components/molecules/toggle-group.tsx (98%) rename {src => packages/common/src}/components/molecules/token-icon.tsx (90%) rename {src => packages/common/src}/components/molecules/tp-sl-dialog.tsx (68%) rename {src => packages/common/src}/components/ui/button.tsx (76%) rename {src => packages/common/src}/components/ui/card.tsx (87%) rename {src => packages/common/src}/components/ui/dialog.tsx (96%) create mode 100644 packages/common/src/components/ui/divider.tsx create mode 100644 packages/common/src/components/ui/popover.tsx create mode 100644 packages/common/src/components/ui/select.tsx rename {src => packages/common/src}/components/ui/skeleton.tsx (96%) rename {src => packages/common/src}/components/ui/slider.tsx (99%) rename {src => packages/common/src}/components/ui/spinner.tsx (94%) rename {src => packages/common/src}/components/ui/tabs.tsx (95%) create mode 100644 packages/common/src/components/ui/text.tsx rename {src => packages/common/src}/components/ui/toaster.tsx (100%) rename {src => packages/common/src}/context/appkit.tsx (87%) rename {src => packages/common/src}/context/index.tsx (62%) rename {src => packages/common/src}/context/root-container.tsx (100%) rename {src => packages/common/src}/domain/chains/evm.ts (100%) create mode 100644 packages/common/src/domain/chains/index.ts rename {src => packages/common/src}/domain/chains/ledger.ts (95%) create mode 100644 packages/common/src/domain/index.ts rename {src => packages/common/src}/domain/signer.ts (96%) rename {src => packages/common/src}/domain/tokens.ts (92%) rename {src => packages/common/src}/domain/transactions.ts (95%) rename {src => packages/common/src}/domain/types.ts (79%) rename {src => packages/common/src}/domain/wallet.ts (94%) create mode 100644 packages/common/src/hooks/index.ts rename {src => packages/common/src}/hooks/use-order-actions.ts (91%) rename {src => packages/common/src}/hooks/use-position-actions.ts (93%) rename {src => packages/common/src}/hooks/use-tp-sl-orders.ts (93%) create mode 100644 packages/common/src/lib/formatting.ts create mode 100644 packages/common/src/lib/index.ts create mode 100644 packages/common/src/lib/math.ts create mode 100644 packages/common/src/lib/utils.ts rename {src => packages/common/src}/services/api-client/api-schemas.ts (81%) rename {src => packages/common/src}/services/api-client/client-factory.ts (83%) rename {src => packages/common/src}/services/api-client/index.ts (94%) rename {src => packages/common/src}/services/config.ts (97%) rename {src => packages/common/src}/services/constants.ts (100%) rename src/services/http-client/index.tsx => packages/common/src/services/http-client/index.ts (100%) create mode 100644 packages/common/src/services/index.ts rename {src => packages/common/src}/services/runtime.ts (68%) rename {src => packages/common/src}/services/wallet/browser-signer.ts (96%) rename {src => packages/common/src}/services/wallet/ledger-signer/index.ts (94%) rename {src => packages/common/src}/services/wallet/ledger-signer/utils.ts (93%) rename {src => packages/common/src}/services/wallet/signer.ts (75%) rename {src => packages/common/src}/services/wallet/wallet-service.ts (97%) create mode 100644 packages/common/src/styles/base.css create mode 100644 packages/common/src/styles/index.css create mode 100644 packages/common/src/styles/theme.css rename vite.config.ts => packages/common/src/vite.config.ts (60%) rename {tests => packages/common/tests}/components/leverage-dialog.test.tsx (99%) rename {tests => packages/common/tests}/components/limit-price-dialog.test.tsx (99%) rename {tests => packages/common/tests}/components/order-type-dialog.test.tsx (99%) rename {tests => packages/common/tests}/components/tp-sl-dialog.test.tsx (99%) rename {tests => packages/common/tests}/components/wrapper.tsx (56%) create mode 100644 packages/common/tsconfig.json create mode 100644 packages/common/vite.config.ts create mode 100644 packages/dashboard/.env.example create mode 100644 packages/dashboard/index.html create mode 100644 packages/dashboard/package.json rename {public => packages/dashboard/public}/apple-touch-icon-180x180.png (100%) rename {public => packages/dashboard/public}/favicon.ico (100%) rename {public => packages/dashboard/public}/logo-192x192.png (100%) rename {public => packages/dashboard/public}/logo-512x512.png (100%) rename {public => packages/dashboard/public}/logo-64x64.png (100%) rename {public => packages/dashboard/public}/maskable-icon-512x512.png (100%) rename {public => packages/dashboard/public}/yield_xyz.png (100%) create mode 100644 packages/dashboard/src/app.tsx create mode 100644 packages/dashboard/src/atoms/selected-market-atom.ts create mode 100644 packages/dashboard/src/components/atoms/logo.tsx create mode 100644 packages/dashboard/src/components/modules/root/Preload.tsx create mode 100644 packages/dashboard/src/components/modules/trade/chart.tsx create mode 100644 packages/dashboard/src/components/modules/trade/index.tsx create mode 100644 packages/dashboard/src/components/modules/trade/market-info/bar-skeleton.tsx create mode 100644 packages/dashboard/src/components/modules/trade/market-info/index.tsx create mode 100644 packages/dashboard/src/components/modules/trade/market-info/market-selector-popover.tsx create mode 100644 packages/dashboard/src/components/modules/trade/order-form/index.tsx create mode 100644 packages/dashboard/src/components/modules/trade/order-form/sign-dialog.tsx create mode 100644 packages/dashboard/src/components/modules/trade/order-form/state.tsx create mode 100644 packages/dashboard/src/components/molecules/header/deposit/deposit-dialog.tsx create mode 100644 packages/dashboard/src/components/molecules/header/deposit/state.tsx create mode 100644 packages/dashboard/src/components/molecules/header/index.tsx create mode 100644 packages/dashboard/src/components/molecules/header/withdraw/state.tsx create mode 100644 packages/dashboard/src/components/molecules/header/withdraw/withdraw-dialog.tsx create mode 100644 packages/dashboard/src/components/molecules/positions/close-position-dialog.tsx create mode 100644 packages/dashboard/src/components/molecules/positions/index.tsx create mode 100644 packages/dashboard/src/components/molecules/positions/orders-tab.tsx create mode 100644 packages/dashboard/src/components/molecules/positions/positions-tab.tsx create mode 100644 packages/dashboard/src/components/molecules/positions/shared.tsx create mode 100644 packages/dashboard/src/components/molecules/positions/state.ts create mode 100644 packages/dashboard/src/main.css rename {src => packages/dashboard/src}/main.tsx (76%) create mode 100644 packages/dashboard/src/routeTree.gen.ts create mode 100644 packages/dashboard/src/routes/__root.tsx create mode 100644 packages/dashboard/src/routes/index.tsx create mode 100644 packages/dashboard/src/styles.css create mode 100644 packages/dashboard/tsconfig.json create mode 100644 packages/dashboard/vite.config.ts create mode 100644 packages/widget/.env.example create mode 100644 packages/widget/index.html create mode 100644 packages/widget/package.json create mode 100644 packages/widget/public/apple-touch-icon-180x180.png create mode 100644 packages/widget/public/favicon.ico create mode 100644 packages/widget/public/logo-192x192.png create mode 100644 packages/widget/public/logo-512x512.png create mode 100644 packages/widget/public/logo-64x64.png create mode 100644 packages/widget/public/maskable-icon-512x512.png create mode 100644 packages/widget/public/yield_xyz.png rename {src => packages/widget/src}/app.tsx (59%) rename {src => packages/widget/src}/components/modules/Account/Deposit/index.tsx (80%) rename {src => packages/widget/src}/components/modules/Account/Deposit/sign.tsx (59%) rename {src => packages/widget/src}/components/modules/Account/Deposit/state.tsx (83%) rename {src => packages/widget/src}/components/modules/Account/Withdraw/index.tsx (79%) rename {src => packages/widget/src}/components/modules/Account/Withdraw/sign.tsx (59%) rename {src => packages/widget/src}/components/modules/Account/Withdraw/state.tsx (81%) rename {src => packages/widget/src}/components/modules/Account/balance.tsx (78%) rename {src => packages/widget/src}/components/modules/Home/AssetList/index.tsx (86%) rename {src => packages/widget/src}/components/modules/Home/AssetList/item.tsx (62%) rename {src => packages/widget/src}/components/modules/Home/Positions/index.tsx (78%) rename {src => packages/widget/src}/components/modules/Home/Positions/order-card.tsx (59%) rename {src => packages/widget/src}/components/modules/Home/Positions/position-card.tsx (61%) rename {src => packages/widget/src}/components/modules/Home/index.tsx (79%) rename {src => packages/widget/src}/components/modules/Order/Overview/index.tsx (70%) rename {src => packages/widget/src}/components/modules/Order/Overview/loading.tsx (92%) rename {src => packages/widget/src}/components/modules/Order/Overview/state.tsx (83%) rename {src => packages/widget/src}/components/modules/Order/sign.tsx (57%) rename {src => packages/widget/src}/components/modules/PositionDetails/Close/index.tsx (71%) rename {src => packages/widget/src}/components/modules/PositionDetails/Close/sign.tsx (59%) rename {src => packages/widget/src}/components/modules/PositionDetails/Close/state.tsx (82%) rename {src => packages/widget/src}/components/modules/PositionDetails/Overview/CancelOrder/sign.tsx (60%) rename {src => packages/widget/src}/components/modules/PositionDetails/Overview/EditLeverage/sign.tsx (61%) rename {src => packages/widget/src}/components/modules/PositionDetails/Overview/Orders/index.tsx (68%) rename {src => packages/widget/src}/components/modules/PositionDetails/Overview/Position/index.tsx (71%) rename {src => packages/widget/src}/components/modules/PositionDetails/Overview/Position/sign.tsx (58%) rename {src => packages/widget/src}/components/modules/PositionDetails/Overview/Position/state.ts (76%) rename {src => packages/widget/src}/components/modules/PositionDetails/Overview/index.tsx (68%) rename {src => packages/widget/src}/components/modules/PositionDetails/Overview/loading.tsx (92%) rename {src => packages/widget/src}/components/modules/PositionDetails/Overview/modify-dialog.tsx (79%) create mode 100644 packages/widget/src/components/modules/PositionDetails/Overview/overview-tab-content.tsx rename {src => packages/widget/src}/components/modules/Root/PreloadAtoms.tsx (70%) rename {src => packages/widget/src}/components/molecules/account-value-display.tsx (62%) rename {src => packages/widget/src}/components/molecules/navigation/back-button.tsx (84%) rename {src => packages/widget/src}/components/molecules/navigation/wallet-protected-route.tsx (73%) rename {src => packages/widget/src}/components/molecules/provider-select.tsx (82%) rename {src => packages/widget/src}/components/molecules/sign/index.tsx (67%) create mode 100644 packages/widget/src/components/molecules/sign/sign-content.tsx rename {src => packages/widget/src}/components/molecules/token-balances-select.tsx (91%) create mode 100644 packages/widget/src/main.css create mode 100644 packages/widget/src/main.tsx rename {src => packages/widget/src}/routeTree.gen.ts (100%) rename {src => packages/widget/src}/routes/__root.tsx (100%) rename {src => packages/widget/src}/routes/account/deposit.tsx (66%) rename {src => packages/widget/src}/routes/account/deposit_/sign.tsx (65%) rename {src => packages/widget/src}/routes/account/index.tsx (65%) rename {src => packages/widget/src}/routes/account/withdraw.tsx (65%) rename {src => packages/widget/src}/routes/account/withdraw_/sign.tsx (64%) rename {src => packages/widget/src}/routes/index.tsx (71%) rename {src => packages/widget/src}/routes/order/$marketId/$side/increase_/index.tsx (85%) rename {src => packages/widget/src}/routes/order/$marketId/$side/index.tsx (85%) rename {src => packages/widget/src}/routes/order/$marketId/$side/sign.tsx (68%) rename {src => packages/widget/src}/routes/position-details/$marketId/cancel-order_/sign.tsx (62%) rename {src => packages/widget/src}/routes/position-details/$marketId/close.tsx (66%) rename {src => packages/widget/src}/routes/position-details/$marketId/close_/sign.tsx (66%) rename {src => packages/widget/src}/routes/position-details/$marketId/edit-leverage_/index.tsx (62%) rename {src => packages/widget/src}/routes/position-details/$marketId/edit-sl-tp_/sign.tsx (63%) rename {src => packages/widget/src}/routes/position-details/$marketId/index.tsx (80%) create mode 100644 packages/widget/src/styles.css create mode 100644 packages/widget/tsconfig.json create mode 100644 packages/widget/vite.config.ts delete mode 100644 src/atoms/config-atom.ts delete mode 100644 src/components/modules/Order/Overview/utils.ts delete mode 100644 src/components/modules/PositionDetails/Overview/overview-tab-content.tsx delete mode 100644 src/components/ui/divider.tsx delete mode 100644 src/domain/chains/index.ts delete mode 100644 src/domain/market.ts delete mode 100644 src/domain/position.ts delete mode 100644 src/lib/utils.ts delete mode 100644 src/main.css delete mode 100644 src/styles.css create mode 100644 tsconfig.base.json create mode 100644 turbo.json diff --git a/.cursor/worktrees.json b/.cursor/worktrees.json deleted file mode 100644 index bf9e730..0000000 --- a/.cursor/worktrees.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "setup-worktree": ["pnpm install"] -} diff --git a/.gitignore b/.gitignore index feb03c5..c2283c8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ dist-ssr # opensrc - source code for packages opensrc/ + +.turbo +*.tsbuildinfo \ No newline at end of file diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..38bf73a --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +pnpm commitlint --edit ${1} diff --git a/biome.json b/biome.json index 6b38be3..ee7c6a1 100644 --- a/biome.json +++ b/biome.json @@ -14,7 +14,8 @@ "!**/.vscode", "!**/routeTree.gen.ts", "!**/client-factory.ts", - "!**/api-schemas.ts" + "!**/api-schemas.ts", + "!**/opensrc" ] }, "formatter": { diff --git a/commitlint.config.cjs b/commitlint.config.cjs new file mode 100644 index 0000000..69b4242 --- /dev/null +++ b/commitlint.config.cjs @@ -0,0 +1,3 @@ +module.exports = { + extends: ["@commitlint/config-conventional"], +}; diff --git a/components.json b/components.json deleted file mode 100644 index 7934967..0000000 --- a/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "base-vega", - "rsc": false, - "tsx": true, - "tailwind": { - "config": "", - "css": "src/styles.css", - "baseColor": "zinc", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} diff --git a/index.html b/index.html index b92d082..0e4aacb 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,6 @@
- + diff --git a/package.json b/package.json index 5fdc9be..01ec7e0 100644 --- a/package.json +++ b/package.json @@ -1,71 +1,29 @@ { - "name": "perps", - "private": true, - "type": "module", + "name": "@yieldxyz/perps", + "keywords": [ + "perps", + "yieldxyz" + ], "scripts": { - "dev": "vite --port 3000", - "build": "vite build && tsc", - "preview": "vite preview", - "test": "vitest run", - "lint": "biome check . && tsc", - "format": "biome format --write .", - "generate-client-factory": "tsx --env-file .env scripts/generate-client-factory.ts", - "generate-routes": "tsr generate", - "generate-tradingview-symbols": "tsx --env-file .env scripts/generate-tradingview-symbols/index.ts" - }, - "dependencies": { - "@base-ui/react": "^1.1.0", - "@effect-atom/atom-react": "^0.4.6", - "@effect/experimental": "^0.58.0", - "@effect/platform": "^0.94.2", - "@effect/platform-node": "^0.104.1", - "@ledgerhq/wallet-api-client": "^1.12.6", - "@lucas-barake/effect-form-react": "^0.14.0", - "@reown/appkit": "^1.8.17", - "@reown/appkit-adapter-wagmi": "^1.8.17", - "@stakekit/common": "^0.0.61", - "@tailwindcss/vite": "^4.0.6", - "@tanstack/react-devtools": "^0.9.2", - "@tanstack/react-query": "^5.90.20", - "@tanstack/react-router": "^1.157.8", - "@tanstack/react-router-devtools": "^1.157.8", - "@tanstack/react-virtual": "^3.13.18", - "@tanstack/router-plugin": "^1.157.8", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "effect": "^3.19.15", - "lucide-react": "^0.563.0", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "sonner": "^2.0.7", - "tailwind-merge": "^3.0.2", - "tailwindcss": "^4.0.6", - "tw-animate-css": "^1.3.6", - "viem": "^2.45.0", - "wagmi": "^3.4.1" + "dev": "turbo dev --filter=@yieldxyz/perps-common --filter=@yieldxyz/perps-widget --filter=@yieldxyz/perps-dashboard", + "dev:widget": "turbo dev --filter=@yieldxyz/perps-common --filter=@yieldxyz/perps-widget", + "dev:dashboard": "turbo dev --filter=@yieldxyz/perps-common --filter=@yieldxyz/perps-dashboard", + + "build:widget": "turbo build --filter=@yieldxyz/perps-widget", + "build:dashboard": "turbo build --filter=@yieldxyz/perps-dashboard", + + "build": "turbo build", + "test": "turbo test", + "lint": "turbo lint", + "format": "turbo format", + "prepare": "husky" }, "devDependencies": { - "@biomejs/biome": "2.3.12", - "@effect/language-service": "^0.72.0", - "@tanstack/devtools-vite": "^0.4.1", - "@tanstack/router-cli": "^1.157.15", - "@testing-library/dom": "^10.4.0", - "@testing-library/react": "^16.3.2", - "@tim-smart/openapi-gen": "^0.4.13", - "@types/node": "^25.0.10", - "@types/react": "^19.2.9", - "@types/react-dom": "^19.2.0", - "@vite-pwa/assets-generator": "^1.0.2", - "@vitejs/plugin-react": "^5.0.4", - "@vitest/browser-playwright": "^4.0.18", - "babel-plugin-react-compiler": "^1.0.0", - "jsdom": "^27.0.0", - "openapi-filter": "^3.2.3", - "tsx": "^4.21.0", - "typescript": "^5.7.2", - "vite": "^7.3.1", - "vite-plugin-node-polyfills": "^0.25.0", - "vitest": "^4.0.18", - "vitest-browser-react": "^2.0.4" + "turbo": "catalog:", + "@biomejs/biome": "catalog:", + "@effect/language-service": "catalog:", + "@commitlint/cli": "catalog:", + "@commitlint/config-conventional": "catalog:", + "husky": "catalog:" } } diff --git a/packages/common/package.json b/packages/common/package.json new file mode 100644 index 0000000..7933729 --- /dev/null +++ b/packages/common/package.json @@ -0,0 +1,108 @@ +{ + "name": "@yieldxyz/perps-common", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "pnpm run watch", + "watch": "tsc -b --watch", + "build": "rm -rf dist && tsc -b && pnpm run copy-assets", + "test": "vitest run", + "copy-assets": "cp -r src/assets dist/src && cp -r src/styles dist/src/styles", + "lint": "biome check . && tsc -b", + "format": "biome format --write .", + "generate-client-factory": "tsx --env-file .env scripts/generate-client-factory.ts", + "generate-tradingview-symbols": "tsx --env-file .env scripts/generate-tradingview-symbols/index.ts" + }, + "exports": { + "./vite.config": { + "default": "./dist/src/vite.config.js" + }, + "./styles": { + "default": "./dist/src/styles/index.css" + }, + "./assets/*": { + "default": "./dist/src/assets/*" + }, + "./services": { + "types": "./dist/src/services/index.d.ts", + "default": "./dist/src/services/index.js" + }, + "./domain": { + "types": "./dist/src/domain/index.d.ts", + "default": "./dist/src/domain/index.js" + }, + "./components": { + "types": "./dist/src/components/index.d.ts", + "default": "./dist/src/components/index.js" + }, + "./context": { + "types": "./dist/src/context/index.d.ts", + "default": "./dist/src/context/index.js" + }, + "./atoms": { + "types": "./dist/src/atoms/index.d.ts", + "default": "./dist/src/atoms/index.js" + }, + "./hooks": { + "types": "./dist/src/hooks/index.d.ts", + "default": "./dist/src/hooks/index.js" + }, + "./lib": { + "types": "./dist/src/lib/index.d.ts", + "default": "./dist/src/lib/index.js" + } + }, + "dependencies": { + "@base-ui/react": "catalog:", + "@effect-atom/atom-react": "catalog:", + "@effect/experimental": "^0.58.0", + "@effect/platform": "catalog:", + "@effect/platform-node": "catalog:", + "@ledgerhq/wallet-api-client": "catalog:", + "@lucas-barake/effect-form-react": "catalog:", + "@reown/appkit": "catalog:", + "@reown/appkit-adapter-wagmi": "catalog:", + "@stakekit/common": "catalog:", + "@tailwindcss/vite": "catalog:", + "@tanstack/react-devtools": "catalog:", + "@tanstack/react-query": "catalog:", + "@tanstack/react-router": "catalog:", + "@tanstack/react-router-devtools": "catalog:", + "@tanstack/react-virtual": "catalog:", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "effect": "catalog:", + "lucide-react": "catalog:", + "react": "catalog:", + "react-dom": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", + "tailwindcss": "catalog:", + "tw-animate-css": "catalog:", + "viem": "catalog:", + "wagmi": "catalog:" + }, + "devDependencies": { + "@tanstack/devtools-vite": "catalog:", + "@tanstack/router-cli": "catalog:", + "@testing-library/dom": "catalog:", + "@testing-library/react": "catalog:", + "@tim-smart/openapi-gen": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "@vite-pwa/assets-generator": "catalog:", + "@vitejs/plugin-react": "catalog:", + "@vitest/browser-playwright": "catalog:", + "babel-plugin-react-compiler": "catalog:", + "jsdom": "catalog:", + "openapi-filter": "catalog:", + "tsx": "catalog:", + "typescript": "catalog:", + "vite": "catalog:", + "vite-plugin-node-polyfills": "catalog:", + "vitest": "catalog:", + "vitest-browser-react": "catalog:" + } +} diff --git a/scripts/generate-client-factory.ts b/packages/common/scripts/generate-client-factory.ts similarity index 97% rename from scripts/generate-client-factory.ts rename to packages/common/scripts/generate-client-factory.ts index 33eab10..0b8351e 100644 --- a/scripts/generate-client-factory.ts +++ b/packages/common/scripts/generate-client-factory.ts @@ -14,7 +14,7 @@ const fetchOpenApiSpecs = Effect.gen(function* () { const client = yield* HttpClient.HttpClient; const fs = yield* FileSystem.FileSystem; - const perpsDocsUrl = yield* Config.string("VITE_PERPS_DOCS_URL"); + const perpsDocsUrl = yield* Config.string("PERPS_DOCS_URL"); const perpsJsonPath = yield* perpsOpenApiJsonPath; yield* client.get(perpsDocsUrl).pipe( diff --git a/scripts/generate-tradingview-symbols/index.ts b/packages/common/scripts/generate-tradingview-symbols/index.ts similarity index 97% rename from scripts/generate-tradingview-symbols/index.ts rename to packages/common/scripts/generate-tradingview-symbols/index.ts index 3e09b62..12abbcd 100644 --- a/scripts/generate-tradingview-symbols/index.ts +++ b/packages/common/scripts/generate-tradingview-symbols/index.ts @@ -19,7 +19,7 @@ import { normalizeSymbol, ProvidersResponse, TradingViewSearchResponse, -} from "scripts/generate-tradingview-symbols/utils"; +} from "./utils"; // ----------------------------------------------------------------------------- // HttpClient Services @@ -29,8 +29,8 @@ class PerpsClient extends Effect.Service()( { dependencies: [FetchHttpClient.layer], effect: Effect.gen(function* () { - const baseUrl = yield* Config.string("VITE_PERPS_BASE_URL"); - const apiKey = yield* Config.string("VITE_PERPS_API_KEY"); + const baseUrl = yield* Config.string("PERPS_BASE_URL"); + const apiKey = yield* Config.string("PERPS_API_KEY"); const client = yield* HttpClient.HttpClient; return client.pipe( @@ -247,7 +247,6 @@ const program = Effect.gen(function* () { const path = join( dirname(fileURLToPath(import.meta.url)), "..", - "..", "src", "assets", "tradingview-symbols.json", diff --git a/scripts/generate-tradingview-symbols/utils.ts b/packages/common/scripts/generate-tradingview-symbols/utils.ts similarity index 100% rename from scripts/generate-tradingview-symbols/utils.ts rename to packages/common/scripts/generate-tradingview-symbols/utils.ts diff --git a/src/assets/hyperliquid.png b/packages/common/src/assets/hyperliquid.png similarity index 100% rename from src/assets/hyperliquid.png rename to packages/common/src/assets/hyperliquid.png diff --git a/src/assets/tradingview-symbols.json b/packages/common/src/assets/tradingview-symbols.json similarity index 99% rename from src/assets/tradingview-symbols.json rename to packages/common/src/assets/tradingview-symbols.json index 1098d8d..d9cb0c4 100644 --- a/src/assets/tradingview-symbols.json +++ b/packages/common/src/assets/tradingview-symbols.json @@ -128,7 +128,7 @@ { "status": "match", "perpsSymbol": "SNX", - "tradingViewSymbol": "SNXUSDC", + "tradingViewSymbol": "SNXUSD", "providerId": "BINANCE" }, { @@ -417,7 +417,7 @@ "status": "match", "perpsSymbol": "MAV", "tradingViewSymbol": "MAVUSD", - "providerId": "BINANCE" + "providerId": "TVC" }, { "status": "match", @@ -825,7 +825,7 @@ "status": "match", "perpsSymbol": "VINE", "tradingViewSymbol": "VINEUSD", - "providerId": "KRAKEN" + "providerId": "CRYPTOCOM" }, { "status": "match", diff --git a/src/atoms/actions-atoms.ts b/packages/common/src/atoms/actions-atoms.ts similarity index 62% rename from src/atoms/actions-atoms.ts rename to packages/common/src/atoms/actions-atoms.ts index e0dc0a5..ed2cbf5 100644 --- a/src/atoms/actions-atoms.ts +++ b/packages/common/src/atoms/actions-atoms.ts @@ -1,12 +1,12 @@ import { Reactivity } from "@effect/experimental/Reactivity"; -import { Atom } from "@effect-atom/atom-react"; +import { Atom, type Result } from "@effect-atom/atom-react"; import { Effect, Stream } from "effect"; -import { portfolioReactivityKeysArray } from "@/atoms/portfolio-atoms"; -import type { WalletConnected } from "@/domain/wallet"; -import type { ActionDto } from "@/services/api-client/api-schemas"; -import { runtimeAtom } from "@/services/runtime"; +import type { SignTransactionsState, WalletConnected } from "../domain/wallet"; +import type { ActionDto } from "../services/api-client/api-schemas"; +import { runtimeAtom } from "../services/runtime"; +import { portfolioReactivityKeysArray } from "./portfolio-atoms"; -export const actionAtom = Atom.writable( +export const actionAtom = Atom.writable( () => null, (ctx, value) => ctx.setSelf(value), ); @@ -21,7 +21,21 @@ const getActionAtom = Atom.make( }), ); -export const signActionAtoms = Atom.family( +type SignActionAtoms = ( + arg: (action: ActionDto) => Effect.Effect< + { + stream: Stream.Stream; + retry: Effect.Effect; + }, + never, + never + >, +) => { + machineStreamAtom: Atom.Atom>; + retryMachineAtom: Atom.AtomResultFn; +}; + +export const signActionAtoms: SignActionAtoms = Atom.family( (signTransactions: WalletConnected["signTransactions"]) => { const machineAtom = runtimeAtom.atom((ctx) => ctx diff --git a/packages/common/src/atoms/config-atom.ts b/packages/common/src/atoms/config-atom.ts new file mode 100644 index 0000000..86681ed --- /dev/null +++ b/packages/common/src/atoms/config-atom.ts @@ -0,0 +1,4 @@ +import { ConfigService } from "../services/config"; +import { runtimeAtom } from "../services/runtime"; + +export const configAtom = runtimeAtom.atom(ConfigService); diff --git a/packages/common/src/atoms/index.ts b/packages/common/src/atoms/index.ts new file mode 100644 index 0000000..4d5d933 --- /dev/null +++ b/packages/common/src/atoms/index.ts @@ -0,0 +1,10 @@ +export * from "./actions-atoms"; +export * from "./config-atom"; +export * from "./markets-atoms"; +export * from "./orders-pending-actions-atom"; +export * from "./portfolio-atoms"; +export * from "./position-pending-actions-atom"; +export * from "./providers-atoms"; +export * from "./tokens-atoms"; +export * from "./utils"; +export * from "./wallet-atom"; diff --git a/src/atoms/markets-atoms.ts b/packages/common/src/atoms/markets-atoms.ts similarity index 77% rename from src/atoms/markets-atoms.ts rename to packages/common/src/atoms/markets-atoms.ts index a018e89..7b115e0 100644 --- a/src/atoms/markets-atoms.ts +++ b/packages/common/src/atoms/markets-atoms.ts @@ -1,9 +1,9 @@ import { Atom, AtomRef } from "@effect-atom/atom-react"; import { Data, Duration, Effect, Record, Schedule, Stream } from "effect"; -import { selectedProviderAtom } from "@/atoms/providers-atoms"; -import { ApiClientService } from "@/services/api-client"; -import type { ProviderDto } from "@/services/api-client/api-schemas"; -import { runtimeAtom } from "@/services/runtime"; +import { ApiClientService } from "../services/api-client"; +import type { ProviderDto } from "../services/api-client/api-schemas"; +import { runtimeAtom } from "../services/runtime"; +import { selectedProviderAtom } from "./providers-atoms"; const DEFAULT_LIMIT = 50; @@ -37,20 +37,18 @@ const getAllMarkets = Effect.fn(function* (selectedProvider: ProviderDto) { ]; }); -export const marketsAtom = runtimeAtom - .atom( - Effect.fn(function* (ctx) { - const selectedProvider = yield* ctx.result(selectedProviderAtom); +export const marketsAtom = runtimeAtom.atom( + Effect.fn(function* (ctx) { + const selectedProvider = yield* ctx.result(selectedProviderAtom); - const markets = yield* getAllMarkets(selectedProvider); + const markets = yield* getAllMarkets(selectedProvider); - return Record.fromIterableBy( - markets.map((market) => AtomRef.make(market)), - (m) => m.value.id, - ); - }), - ) - .pipe(Atom.keepAlive); + return Record.fromIterableBy( + markets.map((market) => AtomRef.make(market)), + (m) => m.value.id, + ); + }), +); export class MarketNotFoundError extends Data.TaggedError( "MarketNotFoundError", diff --git a/src/atoms/orders-pending-actions-atom.ts b/packages/common/src/atoms/orders-pending-actions-atom.ts similarity index 77% rename from src/atoms/orders-pending-actions-atom.ts rename to packages/common/src/atoms/orders-pending-actions-atom.ts index 9976f69..ebda7bc 100644 --- a/src/atoms/orders-pending-actions-atom.ts +++ b/packages/common/src/atoms/orders-pending-actions-atom.ts @@ -1,10 +1,10 @@ import { Atom, Registry, Result } from "@effect-atom/atom-react"; import { Effect } from "effect"; -import { actionAtom } from "@/atoms/actions-atoms"; -import { selectedProviderAtom } from "@/atoms/providers-atoms"; -import type { WalletAccount } from "@/domain/wallet"; -import { ApiClientService } from "@/services/api-client"; -import { runtimeAtom } from "@/services/runtime"; +import type { WalletAccount } from "../domain/wallet"; +import { ApiClientService } from "../services/api-client"; +import { runtimeAtom } from "../services/runtime"; +import { actionAtom } from "./actions-atoms"; +import { selectedProviderAtom } from "./providers-atoms"; export const cancelOrderAtom = Atom.family((orderId: string) => runtimeAtom.fn( diff --git a/src/atoms/portfolio-atoms.ts b/packages/common/src/atoms/portfolio-atoms.ts similarity index 90% rename from src/atoms/portfolio-atoms.ts rename to packages/common/src/atoms/portfolio-atoms.ts index 4e8b6fb..ce4cb84 100644 --- a/src/atoms/portfolio-atoms.ts +++ b/packages/common/src/atoms/portfolio-atoms.ts @@ -1,10 +1,10 @@ import { Atom } from "@effect-atom/atom-react"; import { Duration, Effect } from "effect"; -import { providersAtom, selectedProviderAtom } from "@/atoms/providers-atoms"; -import { withRefreshAfter } from "@/atoms/utils"; -import type { WalletAccount } from "@/domain/wallet"; -import { ApiClientService } from "@/services/api-client"; -import { runtimeAtom, withReactivity } from "@/services/runtime"; +import type { WalletAccount } from "../domain/wallet"; +import { ApiClientService } from "../services/api-client"; +import { runtimeAtom, withReactivity } from "../services/runtime"; +import { providersAtom, selectedProviderAtom } from "./providers-atoms"; +import { withRefreshAfter } from "./utils"; export const portfolioReactivityKeys = { positions: "positions", diff --git a/src/atoms/position-pending-actions-atom.ts b/packages/common/src/atoms/position-pending-actions-atom.ts similarity index 86% rename from src/atoms/position-pending-actions-atom.ts rename to packages/common/src/atoms/position-pending-actions-atom.ts index 58fdf59..93566e6 100644 --- a/src/atoms/position-pending-actions-atom.ts +++ b/packages/common/src/atoms/position-pending-actions-atom.ts @@ -1,18 +1,18 @@ import { Registry, Result } from "@effect-atom/atom-react"; import { Effect } from "effect"; -import { actionAtom } from "@/atoms/actions-atoms"; -import { selectedProviderAtom } from "@/atoms/providers-atoms"; import type { TPOrSLOption, TPOrSLSettings, -} from "@/components/molecules/tp-sl-dialog"; -import type { WalletAccount, WalletConnected } from "@/domain/wallet"; -import { ApiClientService } from "@/services/api-client"; +} from "../components/molecules/tp-sl-dialog"; +import type { WalletAccount, WalletConnected } from "../domain/wallet"; +import { ApiClientService } from "../services/api-client"; import type { ArgumentsDto, PositionDto, -} from "@/services/api-client/api-schemas"; -import { runtimeAtom } from "@/services/runtime"; +} from "../services/api-client/api-schemas"; +import { runtimeAtom } from "../services/runtime"; +import { actionAtom } from "./actions-atoms"; +import { selectedProviderAtom } from "./providers-atoms"; export const editSLOrTPAtom = runtimeAtom.fn( Effect.fn(function* ({ diff --git a/src/atoms/providers-atoms.ts b/packages/common/src/atoms/providers-atoms.ts similarity index 50% rename from src/atoms/providers-atoms.ts rename to packages/common/src/atoms/providers-atoms.ts index 008bf72..0765114 100644 --- a/src/atoms/providers-atoms.ts +++ b/packages/common/src/atoms/providers-atoms.ts @@ -1,8 +1,8 @@ import { Atom, Result } from "@effect-atom/atom-react"; -import { Array as _Array, Effect } from "effect"; -import { ApiClientService } from "@/services/api-client"; -import type { ProviderDto } from "@/services/api-client/api-schemas"; -import { runtimeAtom, withReactivity } from "@/services/runtime"; +import { Array as _Array, Data, Effect } from "effect"; +import { ApiClientService } from "../services/api-client"; +import type { ProviderDto } from "../services/api-client/api-schemas"; +import { runtimeAtom, withReactivity } from "../services/runtime"; export const providersReactivityKeys = { providers: "providers", @@ -14,23 +14,31 @@ export const providersReactivityKeysArray = Object.values( export const providersAtom = runtimeAtom .atom( - Effect.gen(function* () { - const client = yield* ApiClientService; - - return yield* client.ProvidersControllerGetProviders(); - }), + ApiClientService.pipe( + Effect.andThen((client) => client.ProvidersControllerGetProviders()), + ), ) .pipe(withReactivity([providersReactivityKeys.providers]), Atom.keepAlive); +export class ProviderNotFoundError extends Data.TaggedError( + "ProviderNotFoundError", +) {} + const initialProviderAtom = runtimeAtom.atom( Effect.fn(function* (ctx) { const providers = yield* ctx.resultOnce(providersAtom); - return yield* _Array.head(providers); + const initialProvider = _Array.head(providers); + + if (initialProvider._tag === "None") { + return yield* new ProviderNotFoundError(); + } + + return initialProvider.value; }), ); export const selectedProviderAtom = Atom.writable( (ctx) => ctx.get(initialProviderAtom), (ctx, value: ProviderDto) => ctx.setSelf(Result.success(value)), -); +).pipe(Atom.keepAlive); diff --git a/src/atoms/tokens-atoms.ts b/packages/common/src/atoms/tokens-atoms.ts similarity index 94% rename from src/atoms/tokens-atoms.ts rename to packages/common/src/atoms/tokens-atoms.ts index e24bd52..0b96e8c 100644 --- a/src/atoms/tokens-atoms.ts +++ b/packages/common/src/atoms/tokens-atoms.ts @@ -2,11 +2,11 @@ import { HttpClientRequest, HttpClientResponse } from "@effect/platform"; import { Atom } from "@effect-atom/atom-react"; import { EvmNetworks } from "@stakekit/common"; import { Array as _Array, Effect, Option, pipe, Record, Schema } from "effect"; -import type { TokenBalance } from "@/domain/types"; -import type { WalletAccount } from "@/domain/wallet"; -import { ConfigService } from "@/services/config"; -import { HttpClientService } from "@/services/http-client"; -import { runtimeAtom, withReactivity } from "@/services/runtime"; +import type { TokenBalance } from "../domain/types"; +import type { WalletAccount } from "../domain/wallet"; +import { ConfigService } from "../services/config"; +import { HttpClientService } from "../services/http-client"; +import { runtimeAtom, withReactivity } from "../services/runtime"; export const tokensReactivityKeys = { tokenBalances: "tokenBalances", diff --git a/src/atoms/utils.ts b/packages/common/src/atoms/utils.ts similarity index 100% rename from src/atoms/utils.ts rename to packages/common/src/atoms/utils.ts diff --git a/src/atoms/wallet-atom.ts b/packages/common/src/atoms/wallet-atom.ts similarity index 88% rename from src/atoms/wallet-atom.ts rename to packages/common/src/atoms/wallet-atom.ts index 02ce412..86fa6f7 100644 --- a/src/atoms/wallet-atom.ts +++ b/packages/common/src/atoms/wallet-atom.ts @@ -3,9 +3,9 @@ import { Effect, Stream } from "effect"; import type { BrowserWalletConnected, LedgerWalletConnected, -} from "@/domain/wallet"; -import { runtimeAtom } from "@/services/runtime"; -import { WalletService } from "@/services/wallet/wallet-service"; +} from "../domain/wallet"; +import { runtimeAtom } from "../services/runtime"; +import { WalletService } from "../services/wallet/wallet-service"; export const walletAtom = runtimeAtom.atom( WalletService.pipe( diff --git a/packages/common/src/components/index.ts b/packages/common/src/components/index.ts new file mode 100644 index 0000000..0b4b88a --- /dev/null +++ b/packages/common/src/components/index.ts @@ -0,0 +1,24 @@ +export * from "./molecules/address-switcher"; +export * from "./molecules/Chart/index"; +export * from "./molecules/forms"; +export * from "./molecules/leverage-dialog"; +export * from "./molecules/limit-price-dialog"; +export * from "./molecules/order-type-dialog"; +export * from "./molecules/percentage-slider"; +export * from "./molecules/sign-transactions"; +export * from "./molecules/toggle-group"; +export * from "./molecules/token-icon"; +export * from "./molecules/tp-sl-dialog"; + +export * from "./ui/button"; +export * from "./ui/card"; +export * from "./ui/dialog"; +export * from "./ui/divider"; +export * from "./ui/popover"; +export * from "./ui/select"; +export * from "./ui/skeleton"; +export * from "./ui/slider"; +export * from "./ui/spinner"; +export * from "./ui/tabs"; +export * from "./ui/text"; +export * from "./ui/toaster"; diff --git a/src/components/modules/PositionDetails/Overview/Chart/index.tsx b/packages/common/src/components/molecules/Chart/index.tsx similarity index 51% rename from src/components/modules/PositionDetails/Overview/Chart/index.tsx rename to packages/common/src/components/molecules/Chart/index.tsx index 4a98ca2..a6b06be 100644 --- a/src/components/modules/PositionDetails/Overview/Chart/index.tsx +++ b/packages/common/src/components/molecules/Chart/index.tsx @@ -2,20 +2,26 @@ import { useAtomValue } from "@effect-atom/atom-react"; import { Option } from "effect"; import { TriangleAlertIcon } from "lucide-react"; import { useLayoutEffect, useRef, useState } from "react"; +import { cn } from "../../../lib"; +import { TRADING_VIEW_WIDGET_SCRIPT_URL } from "../../../services"; +import { Text } from "../../ui/text"; +import { ToggleGroup } from "../toggle-group"; import { CHART_INTERVALS, INITIAL_INTERVAL, tradingViewSymbolAtom, -} from "@/components/modules/PositionDetails/Overview/Chart/state"; -import { ToggleGroup } from "@/components/molecules/toggle-group"; -import { TRADING_VIEW_WIDGET_SCRIPT_URL } from "@/services/constants"; +} from "./state"; -function createTradingViewWidget({ +export type ChartVariant = "widget" | "dashboard"; + +export function createTradingViewWidget({ container, onIframeReady, providerId, tradingViewSymbol, + variant, }: { + variant: ChartVariant; container: HTMLDivElement; providerId: string; tradingViewSymbol: string; @@ -23,6 +29,53 @@ function createTradingViewWidget({ }) { container.innerHTML = ""; + const widgetConfig = { + allow_symbol_change: false, + calendar: false, + details: false, + hide_side_toolbar: true, + hide_top_toolbar: true, + hide_legend: true, + hide_volume: true, + hotlist: false, + interval: INITIAL_INTERVAL, + locale: "en", + save_image: false, + style: "1", + symbol: `${providerId}:${tradingViewSymbol}`, + theme: "dark", + timezone: "exchange", + backgroundColor: "rgba(0, 0, 0, 1)", + gridColor: "rgba(74, 74, 74, 0.51)", + watchlist: [], + withdateranges: false, + compareSymbols: [], + studies: [], + autosize: true, + overrides: { + "paneProperties.vertGridProperties.color": "#363c4e", + "mainSeriesProperties.candleStyle.upColor": "#26a69a", + "mainSeriesProperties.candleStyle.downColor": "#ef5350", + // Add these to hide/change border colors: + "paneProperties.separatorColor": "#000000", // transparent (for volume pane separator), + "scalesProperties.lineColor": "transparent", + "scalesProperties.textColor": "#9598a1", // axis text color + "paneProperties.backgroundType": "solid", + "paneProperties.background": "#000000", + }, + }; + + const config = + variant === "widget" + ? JSON.stringify(widgetConfig) + : JSON.stringify({ + ...widgetConfig, + hide_side_toolbar: false, + hide_top_toolbar: false, + hide_legend: false, + hide_volume: false, + }); + const widgetDiv = document.createElement("div"); widgetDiv.className = "tradingview-widget-container__widget"; widgetDiv.style.height = "100%"; @@ -33,31 +86,7 @@ function createTradingViewWidget({ script.src = TRADING_VIEW_WIDGET_SCRIPT_URL; script.type = "text/javascript"; script.async = true; - script.innerHTML = ` - { - "allow_symbol_change": false, - "calendar": false, - "details": false, - "hide_side_toolbar": true, - "hide_top_toolbar": true, - "hide_legend": true, - "hide_volume": true, - "hotlist": true, - "interval": "${INITIAL_INTERVAL}", - "locale": "en", - "save_image": false, - "style": "1", - "symbol": "${providerId}:${tradingViewSymbol}", - "theme": "dark", - "timezone": "exchange", - "backgroundColor": "rgba(0, 0, 0, 1)", - "gridColor": "rgba(74, 74, 74, 0.51)", - "watchlist": [], - "withdateranges": false, - "compareSymbols": [], - "studies": [], - "autosize": true - }`; + script.innerHTML = config; container.appendChild(script); let charLoadTimeout: NodeJS.Timeout | null = null; @@ -94,7 +123,9 @@ function createTradingViewWidget({ function ChartAvailable({ providerId, tradingViewSymbol, + variant, }: { + variant: ChartVariant; providerId: string; tradingViewSymbol: string; }) { @@ -105,11 +136,13 @@ function ChartAvailable({ const [isLoading, setIsLoading] = useState(true); useLayoutEffect(() => { + setIsLoading(true); const container = containerRef.current; if (!container) return; return createTradingViewWidget({ container, + variant, providerId, tradingViewSymbol, onIframeReady: (iframe) => { @@ -117,7 +150,7 @@ function ChartAvailable({ iframeRef.current = iframe; }, }); - }, [providerId, tradingViewSymbol]); + }, [providerId, tradingViewSymbol, variant]); const handleIntervalChange = (newInterval: string) => { if (newInterval === chartInterval || !iframeRef.current?.contentWindow) { @@ -133,11 +166,21 @@ function ChartAvailable({ return ( <> -
+
{/* Loading overlay */} @@ -145,37 +188,58 @@ function ChartAvailable({
- Loading chart... + + Loading chart... +
)}
-
- -
+ {variant === "widget" && ( +
+ +
+ )} ); } -const ChartNotAvailable = () => { +const ChartNotAvailable = ({ variant }: { variant: ChartVariant }) => { return ( -
+
- Chart not available + + Chart not available +
); }; -export const Chart = ({ symbol }: { symbol: string }) => { +export const Chart = ({ + symbol, + variant, +}: { + symbol: string; + variant: "widget" | "dashboard"; +}) => { const chartMeta = useAtomValue(tradingViewSymbolAtom(symbol)).pipe( Option.map((v) => ({ providerId: v.providerId, @@ -184,5 +248,9 @@ export const Chart = ({ symbol }: { symbol: string }) => { Option.getOrNull, ); - return chartMeta ? : ; + return chartMeta ? ( + + ) : ( + + ); }; diff --git a/src/components/modules/PositionDetails/Overview/Chart/state.ts b/packages/common/src/components/molecules/Chart/state.ts similarity index 77% rename from src/components/modules/PositionDetails/Overview/Chart/state.ts rename to packages/common/src/components/molecules/Chart/state.ts index 9c3ec72..66bfd7c 100644 --- a/src/components/modules/PositionDetails/Overview/Chart/state.ts +++ b/packages/common/src/components/molecules/Chart/state.ts @@ -1,6 +1,6 @@ import { Atom } from "@effect-atom/atom-react"; -import { Record, Schema } from "effect"; -import tradingViewSymbols from "@/assets/tradingview-symbols.json" with { +import { Array as _Array, Option, Record, Schema } from "effect"; +import tradingViewSymbols from "../../../assets/tradingview-symbols.json" with { type: "json", }; @@ -34,4 +34,6 @@ export const CHART_INTERVALS = [ { value: "W", label: "1w" }, ]; -export const INITIAL_INTERVAL = CHART_INTERVALS[0].value; +export const INITIAL_INTERVAL = _Array + .head(CHART_INTERVALS) + .pipe(Option.getOrThrow).value; diff --git a/src/components/molecules/address-switcher.tsx b/packages/common/src/components/molecules/address-switcher.tsx similarity index 85% rename from src/components/molecules/address-switcher.tsx rename to packages/common/src/components/molecules/address-switcher.tsx index 73518a2..10b02a4 100644 --- a/src/components/molecules/address-switcher.tsx +++ b/packages/common/src/components/molecules/address-switcher.tsx @@ -2,21 +2,25 @@ import { useAtomSet } from "@effect-atom/atom-react"; import { useDisconnect } from "@reown/appkit/react"; import { Match } from "effect"; import { Check, ChevronDown, Copy, LogOut, Wallet } from "lucide-react"; +import type React from "react"; import { useEffect, useRef, useState } from "react"; import { switchBrowserAccountAtom, switchLedgerAccountAtom, -} from "@/atoms/wallet-atom"; -import { Button } from "@/components/ui/button"; -import { Dialog } from "@/components/ui/dialog"; +} from "../../atoms/wallet-atom"; +import type { + BrowserWalletConnected, + LedgerWalletConnected, +} from "../../domain/wallet"; import { - type BrowserWalletConnected, isBrowserWalletConnected, isLedgerWalletConnected, - type LedgerWalletConnected, type WalletConnected, -} from "@/domain/wallet"; -import { cn, truncateAddress } from "@/lib/utils"; +} from "../../domain/wallet"; +import { cn, truncateAddress } from "../../lib/utils"; +import { Button } from "../ui/button"; +import { Dialog } from "../ui/dialog"; +import { Text } from "../ui/text"; const LedgerAccountList = ({ wallet, @@ -56,13 +60,13 @@ const LedgerAccountList = ({
- + Account ID: {truncateAddress(account.id)} - + - + Address: {truncateAddress(account.address)} - +
{isCurrentAccount && } @@ -105,9 +109,9 @@ const BrowserAccountList = ({
- + {truncateAddress(account.address)} - +
{isCurrentAccount && } @@ -226,26 +230,29 @@ const DisconnectButton = ({ onDisconnect }: { onDisconnect: () => void }) => { ); }; -export const AddressSwitcher = ({ wallet }: { wallet: WalletConnected }) => { +interface AddressSwitcherProps { + wallet: WalletConnected; + trigger?: React.ReactElement; +} + +export const AddressSwitcher = ({ wallet, trigger }: AddressSwitcherProps) => { const [open, setOpen] = useState(false); return ( ( - - )} + render={ + trigger ?? + ((props) => ( + + )) + } /> diff --git a/src/components/molecules/forms/index.tsx b/packages/common/src/components/molecules/forms/index.tsx similarity index 82% rename from src/components/molecules/forms/index.tsx rename to packages/common/src/components/molecules/forms/index.tsx index 0a746c9..102f2d9 100644 --- a/src/components/molecules/forms/index.tsx +++ b/packages/common/src/components/molecules/forms/index.tsx @@ -1,5 +1,6 @@ import type { FormReact } from "@lucas-barake/effect-form-react"; import { Option } from "effect"; +import { Text } from "../../ui/text"; export const AmountField: FormReact.FieldComponent = ({ field }) => { const onChange: (typeof field)["onChange"] = (newValue) => { @@ -41,11 +42,21 @@ export const AmountField: FormReact.FieldComponent = ({ field }) => { {Option.isSome(field.error) && (
- ! + + ! +
- + {field.error.value} - +
)}
diff --git a/packages/common/src/components/molecules/index.ts b/packages/common/src/components/molecules/index.ts new file mode 100644 index 0000000..67c5a39 --- /dev/null +++ b/packages/common/src/components/molecules/index.ts @@ -0,0 +1,7 @@ +export * from "./leverage-dialog"; +export * from "./limit-price-dialog"; +export * from "./order-type-dialog"; +export * from "./percentage-slider"; +export * from "./toggle-group"; +export * from "./token-icon"; +export * from "./tp-sl-dialog"; diff --git a/src/components/modules/Order/Overview/leverage-dialog.tsx b/packages/common/src/components/molecules/leverage-dialog.tsx similarity index 70% rename from src/components/modules/Order/Overview/leverage-dialog.tsx rename to packages/common/src/components/molecules/leverage-dialog.tsx index f304b19..7348e96 100644 --- a/src/components/modules/Order/Overview/leverage-dialog.tsx +++ b/packages/common/src/components/molecules/leverage-dialog.tsx @@ -1,18 +1,21 @@ import type { DialogRootActions } from "@base-ui/react/dialog"; import { X } from "lucide-react"; import { useRef, useState } from "react"; -import { ToggleGroup } from "@/components/molecules/toggle-group"; -import { Button } from "@/components/ui/button"; -import { Dialog } from "@/components/ui/dialog"; -import { Divider } from "@/components/ui/divider"; -import { Slider } from "@/components/ui/slider"; +import { formatAmount, formatPercentage } from "../../lib/formatting"; import { + generateLeverageButtons, + getLeverageFromPercent, getLeveragePercent, + getLeverageStops, getLiquidationPrice, getPriceChangePercentToLiquidation, - MIN_LEVERAGE, -} from "@/domain/position"; -import { formatAmount, formatPercentage } from "@/lib/utils"; +} from "../../lib/math"; +import { Button } from "../ui/button"; +import { Dialog } from "../ui/dialog"; +import { Divider } from "../ui/divider"; +import { Slider } from "../ui/slider"; +import { Text } from "../ui/text"; +import { ToggleGroup } from "./toggle-group"; type LeverageDialogProps = Pick< LeverageDialogContentProps, @@ -73,11 +76,7 @@ export function LeverageDialogContent({ }: LeverageDialogContentProps) { const [localLeverage, setLocalLeverage] = useState(leverage); - const leverageStops = [ - MIN_LEVERAGE, - Math.round(maxLeverage / 2), - maxLeverage, - ]; + const leverageStops = getLeverageStops(maxLeverage); const leveragePercent = getLeveragePercent({ leverage: localLeverage, @@ -105,12 +104,14 @@ export function LeverageDialogContent({
{/* Header */}
- Leverage - +
); } - -const generateLeverageButtons = (maxLeverage: number): number[] => { - const buttons: number[] = []; - - if (maxLeverage >= 2) { - buttons.push(2); - } - - if (maxLeverage >= 5) { - buttons.push(5); - } - - let value = 10; - while (value <= maxLeverage) { - buttons.push(value); - value *= 2; - } - - if (buttons[buttons.length - 1] !== maxLeverage && maxLeverage > 2) { - buttons.push(maxLeverage); - } - - return [...new Set(buttons)].sort((a, b) => a - b); -}; diff --git a/src/components/modules/Order/Overview/limit-price-dialog.tsx b/packages/common/src/components/molecules/limit-price-dialog.tsx similarity index 88% rename from src/components/modules/Order/Overview/limit-price-dialog.tsx rename to packages/common/src/components/molecules/limit-price-dialog.tsx index 8e87f25..f9578a3 100644 --- a/src/components/modules/Order/Overview/limit-price-dialog.tsx +++ b/packages/common/src/components/molecules/limit-price-dialog.tsx @@ -5,8 +5,10 @@ import clsx from "clsx"; import { Option, Schema } from "effect"; import { X } from "lucide-react"; import { useRef } from "react"; -import { Button } from "@/components/ui/button"; -import { Dialog } from "@/components/ui/dialog"; +import { applyPercentDelta, round } from "../../lib/math"; +import { Button } from "../ui/button"; +import { Dialog } from "../ui/dialog"; +import { Text } from "../ui/text"; const PRICE_QUICK_ADJUSTMENTS = [-1, -2, -5, -10]; @@ -67,9 +69,8 @@ function LimitPriceDialogContent({ const submit = useAtomSet(LimitPriceForm.submit); const handleQuickAdjust = (percent: number) => { - const adjustment = currentPrice * (percent / 100); - const newPrice = currentPrice + adjustment; - setAmount(newPrice.toString()); + const newPrice = applyPercentDelta({ value: currentPrice, percent }); + setAmount(round(newPrice, 2).toString()); }; const handleConfirm = () => @@ -82,9 +83,9 @@ function LimitPriceDialogContent({ {/* Header */}
- + Set limit price - + @@ -179,7 +181,9 @@ const LimitPriceForm = FormReact.make(limitPriceFormBuilder, { )} /> - USD + + USD +
), }, diff --git a/src/components/modules/Order/Overview/order-type-dialog.tsx b/packages/common/src/components/molecules/order-type-dialog.tsx similarity index 84% rename from src/components/modules/Order/Overview/order-type-dialog.tsx rename to packages/common/src/components/molecules/order-type-dialog.tsx index c0e3104..969d89d 100644 --- a/src/components/modules/Order/Overview/order-type-dialog.tsx +++ b/packages/common/src/components/molecules/order-type-dialog.tsx @@ -1,8 +1,10 @@ import type { DialogRootActions } from "@base-ui/react/dialog"; import { Check, ChevronDown } from "lucide-react"; import { useRef } from "react"; -import type { OrderType } from "@/components/modules/Order/Overview/state"; -import { Dialog } from "@/components/ui/dialog"; +import { Dialog } from "../ui/dialog"; +import { Text } from "../ui/text"; + +type OrderType = "market" | "limit"; interface OrderTypeDialogProps { selectedType: OrderType; @@ -49,10 +51,10 @@ export function OrderTypeDialog({ type="button" className="flex items-center gap-2 bg-[#161616] px-3.5 py-2.5 rounded-[11px] h-9" > - + {ORDER_TYPE_OPTIONS.find((opt) => opt.value === selectedType) ?.label ?? "Market"} - + )} @@ -77,12 +79,12 @@ export function OrderTypeDialog({ className="flex items-center gap-2 bg-white/5 px-4 py-4 rounded-[10px] w-full text-left transition-colors hover:bg-white/10 cursor-pointer" >
- + {option.label} - - + + {option.description} - +
{isSelected && ( diff --git a/src/components/molecules/percentage-slider.tsx b/packages/common/src/components/molecules/percentage-slider.tsx similarity index 88% rename from src/components/molecules/percentage-slider.tsx rename to packages/common/src/components/molecules/percentage-slider.tsx index 711ad24..c778881 100644 --- a/src/components/molecules/percentage-slider.tsx +++ b/packages/common/src/components/molecules/percentage-slider.tsx @@ -1,8 +1,5 @@ -import { - ToggleGroup, - type ToggleGroupProps, -} from "@/components/molecules/toggle-group"; -import { Slider } from "@/components/ui/slider"; +import { Slider } from "../ui/slider"; +import { ToggleGroup, type ToggleGroupProps } from "./toggle-group"; const percentageOptions = [ { value: "25", label: "25%" }, diff --git a/src/components/molecules/sign/sign-content.tsx b/packages/common/src/components/molecules/sign-transactions/index.tsx similarity index 74% rename from src/components/molecules/sign/sign-content.tsx rename to packages/common/src/components/molecules/sign-transactions/index.tsx index 63a9ba2..4e2f717 100644 --- a/src/components/molecules/sign/sign-content.tsx +++ b/packages/common/src/components/molecules/sign-transactions/index.tsx @@ -1,5 +1,5 @@ -import { Result, useAtomSet, useAtomValue } from "@effect-atom/atom-react"; -import { Navigate } from "@tanstack/react-router"; +import type { SignTransactionsState } from "@yieldxyz/perps-common/domain"; +import { cn, formatSnakeCase } from "@yieldxyz/perps-common/lib"; import { Cause } from "effect"; import { CheckCircle2, @@ -10,37 +10,21 @@ import { Send, XCircle, } from "lucide-react"; -import type { signActionAtoms } from "@/atoms/actions-atoms"; -import { Button } from "@/components/ui/button"; -import { Card, CardSection } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; -import type { SignTransactionsState } from "@/domain/wallet"; -import { cn, formatSnakeCase } from "@/lib/utils"; +import { Button } from "../../ui/button"; +import { Card, CardSection } from "../../ui/card"; +import { Text } from "../../ui/text"; -interface SignTransactionsProps { +export interface TransactionProgressProps { state: SignTransactionsState; - retry: () => void; -} - -function SignTransactionsWithState({ state, retry }: SignTransactionsProps) { - return ( -
-

- Progress -

- - retry()} /> -
- ); + onRetry?: () => void; + onClose?: () => void; } -function SignTransactionsContent({ +export function TransactionProgress({ state, onRetry, -}: { - state: SignTransactionsState; - onRetry?: () => void; -}) { + onClose, +}: TransactionProgressProps) { const { transactions, currentTxIndex, step, error, isDone } = state; const totalTransactions = transactions.length; @@ -52,11 +36,11 @@ function SignTransactionsContent({
{/* Progress indicator */}
-

+ {isDone ? "All transactions complete" : `Transaction ${currentTxIndex + 1} of ${totalTransactions}`} -

+ {totalTransactions > 1 && (
{transactions.map((tx, idx) => ( @@ -87,9 +71,13 @@ function SignTransactionsContent({
-

+ {getErrorDescription(error)} -

+
@@ -122,12 +110,16 @@ function SignTransactionsContent({ {/* Transaction header */}
-

+ {formatTransactionType(tx.type)} -

-

+ + {formatSnakeCase(tx.network)} -

+
- {/* Retry button when there's an error */} - {error !== null && onRetry && ( - - )} + {/* Action buttons */} +
+ {error !== null && onRetry && ( + + )} + {(isDone || error !== null) && onClose && ( + + )} +
); } @@ -190,25 +189,25 @@ function TransactionStatusBadge({ }) { if (status === "CONFIRMED") { return ( - + Confirmed - + ); } if (status === "FAILED" || status === "NOT_FOUND") { return ( - + Failed - + ); } if (isFuture) { return ( - + Pending - + ); } @@ -220,16 +219,16 @@ function TransactionStatusBadge({ }; return ( - + {stepLabels[step]} - + ); } return ( - + {status} - + ); } @@ -282,16 +281,18 @@ function VerticalProgressStep({ )}
{/* Label on the right */} - {label} - +
); } @@ -348,35 +349,3 @@ function getErrorDescription(error: SignTransactionsState["error"]): string { return "An error occurred"; } - -export function SignTransactions({ - machineAtoms, -}: { - machineAtoms: ReturnType; -}) { - const { machineStreamAtom, retryMachineAtom } = machineAtoms; - const state = useAtomValue(machineStreamAtom); - const retry = useAtomSet(retryMachineAtom); - - const result = Result.all({ state, retry }); - - if (Result.isFailure(result)) { - return ( - <> - - - - ); - } - - if (Result.isSuccess(result)) { - return ( - - ); - } - - return ; -} diff --git a/src/components/molecules/toggle-group.tsx b/packages/common/src/components/molecules/toggle-group.tsx similarity index 98% rename from src/components/molecules/toggle-group.tsx rename to packages/common/src/components/molecules/toggle-group.tsx index ca451c4..1de3e20 100644 --- a/src/components/molecules/toggle-group.tsx +++ b/packages/common/src/components/molecules/toggle-group.tsx @@ -1,7 +1,7 @@ import { Toggle } from "@base-ui/react/toggle"; import { ToggleGroup as BaseToggleGroup } from "@base-ui/react/toggle-group"; import type { ComponentProps } from "react"; -import { cn } from "@/lib/utils"; +import { cn } from "../../lib/utils"; export interface ToggleOption { value: string; diff --git a/src/components/molecules/token-icon.tsx b/packages/common/src/components/molecules/token-icon.tsx similarity index 90% rename from src/components/molecules/token-icon.tsx rename to packages/common/src/components/molecules/token-icon.tsx index 158feec..80d815d 100644 --- a/src/components/molecules/token-icon.tsx +++ b/packages/common/src/components/molecules/token-icon.tsx @@ -1,6 +1,5 @@ import { cva, type VariantProps } from "class-variance-authority"; -import { getNetworkLogo } from "@/lib/utils"; -import type { Networks } from "@/services/api-client/client-factory"; +import { getNetworkLogo } from "../../lib/utils"; const tokenIconVariants = cva("relative flex items-end", { variants: { @@ -38,7 +37,7 @@ const networkBadgeVariants = cva( export type TokenIconProps = { logoURI: string; name: string; - network?: Networks; + network?: string; } & VariantProps; export function TokenIcon({ logoURI, name, network, size }: TokenIconProps) { diff --git a/src/components/molecules/tp-sl-dialog.tsx b/packages/common/src/components/molecules/tp-sl-dialog.tsx similarity index 68% rename from src/components/molecules/tp-sl-dialog.tsx rename to packages/common/src/components/molecules/tp-sl-dialog.tsx index b3fda6a..4cb0045 100644 --- a/src/components/molecules/tp-sl-dialog.tsx +++ b/packages/common/src/components/molecules/tp-sl-dialog.tsx @@ -2,14 +2,23 @@ import type { DialogRootActions } from "@base-ui/react/dialog"; import { Match } from "effect"; import { X } from "lucide-react"; import { useRef, useState } from "react"; -import { Button } from "@/components/ui/button"; -import { Card, CardSection } from "@/components/ui/card"; -import { Dialog } from "@/components/ui/dialog"; -import { formatAmount } from "@/lib/utils"; - -const OPTIONS = [0, 10, 25, 50, 100] as const; - -type TPOrSLPercentageOption = (typeof OPTIONS)[number] | null; +import { formatAmount } from "../../lib/formatting"; +import { + calcTpSlPercentFromTriggerPrice, + calcTpSlTriggerPriceFromPercent, + DEFAULT_TP_SL_PERCENT_OPTIONS, + findMatchingPercentOption, + getTpSlPercentPrefix, + round, +} from "../../lib/math"; +import { Button } from "../ui/button"; +import { Card, CardSection } from "../ui/card"; +import { Dialog } from "../ui/dialog"; +import { Text } from "../ui/text"; + +type TPOrSLPercentageOption = + | (typeof DEFAULT_TP_SL_PERCENT_OPTIONS)[number] + | null; export type TPOrSLConfiguration = { option: TPOrSLPercentageOption; @@ -109,25 +118,12 @@ function TPOrSLDialogContent({ ): TPOrSLConfiguration["triggerPrice"] => { if (option === null || option === 0) return null; - return Match.value({ side, tpOrSl }).pipe( - Match.when( - { side: "short", tpOrSl: "takeProfit" }, - () => entryPrice * (1 - option / 100), - ), - Match.when( - { side: "short", tpOrSl: "stopLoss" }, - () => entryPrice * (1 + option / 100), - ), - Match.when( - { side: "long", tpOrSl: "takeProfit" }, - () => entryPrice * (1 + option / 100), - ), - Match.when( - { side: "long", tpOrSl: "stopLoss" }, - () => entryPrice * (1 - option / 100), - ), - Match.exhaustive, - ); + return calcTpSlTriggerPriceFromPercent({ + entryPrice, + percent: option, + side, + tpOrSl, + }); }; const handleTPOrSLOptionChange = ( @@ -157,27 +153,17 @@ function TPOrSLDialogContent({ })); } - const percentage = Match.value({ side, tpOrSl }).pipe( - Match.when( - { side: "short", tpOrSl: "takeProfit" }, - () => ((entryPrice - triggerPrice) / entryPrice) * 100, - ), - Match.when( - { side: "short", tpOrSl: "stopLoss" }, - () => ((triggerPrice - entryPrice) / entryPrice) * 100, - ), - Match.when( - { side: "long", tpOrSl: "takeProfit" }, - () => ((triggerPrice - entryPrice) / entryPrice) * 100, - ), - Match.when( - { side: "long", tpOrSl: "stopLoss" }, - () => ((entryPrice - triggerPrice) / entryPrice) * 100, - ), - Match.exhaustive, - ); - - const option = findMatchingOption(percentage); + const percentage = calcTpSlPercentFromTriggerPrice({ + entryPrice, + triggerPrice, + side, + tpOrSl, + }); + + const option = findMatchingPercentOption({ + percent: percentage, + options: DEFAULT_TP_SL_PERCENT_OPTIONS, + }); setLocalSettings((prev) => ({ ...prev, @@ -200,27 +186,17 @@ function TPOrSLDialogContent({ })); } - const triggerPrice = Match.value({ side, tpOrSl }).pipe( - Match.when( - { side: "short", tpOrSl: "takeProfit" }, - () => entryPrice * (1 - percentage / 100), - ), - Match.when( - { side: "short", tpOrSl: "stopLoss" }, - () => entryPrice * (1 + percentage / 100), - ), - Match.when( - { side: "long", tpOrSl: "takeProfit" }, - () => entryPrice * (1 + percentage / 100), - ), - Match.when( - { side: "long", tpOrSl: "stopLoss" }, - () => entryPrice * (1 - percentage / 100), - ), - Match.exhaustive, - ); - - const option = findMatchingOption(percentage); + const triggerPrice = calcTpSlTriggerPriceFromPercent({ + entryPrice, + percent: percentage, + side, + tpOrSl, + }); + + const option = findMatchingPercentOption({ + percent: percentage, + options: DEFAULT_TP_SL_PERCENT_OPTIONS, + }); setLocalSettings((prev) => ({ ...prev, @@ -244,9 +220,9 @@ function TPOrSLDialogContent({ {/* Header */}
- + {dialogTitle} - +
-

+ {Match.value(isSingleMode).pipe( Match.when( true, @@ -267,7 +243,7 @@ function TPOrSLDialogContent({ "Pick a percentage gain or loss, or enter a custom trigger price to automatically close your position.", ), )} -

+
{/* Info Card */} @@ -334,7 +310,8 @@ function TPOrSLDialogContent({ {/* Done Button */} @@ -352,13 +329,13 @@ interface InfoRowProps { function InfoRow({ label, value, position }: InfoRowProps) { return ( - + {label} - +
- + {formatAmount(value, { maximumFractionDigits: 0 })} - +
); @@ -386,9 +363,9 @@ function TPOrSLSection({ return (
- + {label} - +
- {OPTIONS.map((option) => ( + {DEFAULT_TP_SL_PERCENT_OPTIONS.map((option) => ( @@ -477,15 +444,19 @@ function TPOrSLInputFields({ inputMode="decimal" value={Match.value(triggerPrice).pipe( Match.when(null, () => ""), - Match.orElse((value) => value.toFixed(2)), + Match.orElse((value) => round(value, 2).toString()), )} onChange={(e) => onTriggerPriceChange(e.target.value)} placeholder="Trigger price" className="w-full h-full bg-transparent text-white text-sm font-normal tracking-[-0.42px] pl-4 pr-10 outline-none placeholder:text-gray-2" /> - + ($) - +
""), Match.when(0, () => ""), - Match.orElse((value) => value.toFixed(2)), + Match.orElse((value) => round(value, 2).toString()), )} onChange={(e) => onPercentChange(e.target.value)} placeholder={percentPlaceholder} className="w-full h-full bg-transparent text-white text-sm font-normal tracking-[-0.42px] pl-4 pr-8 outline-none placeholder:text-gray-2" /> - + % - +
); } - -const findMatchingOption = (percent: number): TPOrSLPercentageOption | null => - OPTIONS.find((opt) => opt !== null && Math.abs(percent - opt) < 0.5) || null; - -export const getTPOrSLConfigurationFromPosition = ({ - amount, - entryPrice, - tpOrSl, - side = "long", -}: { - entryPrice: number; - amount: number | undefined; - tpOrSl: TPOrSLOption; - side?: "long" | "short"; -}): TPOrSLConfiguration => { - const percentage = Match.value(amount).pipe( - Match.when(undefined, () => null), - Match.orElse((value) => - Match.value({ side, tpOrSl }).pipe( - Match.when( - { side: "short", tpOrSl: "takeProfit" }, - () => ((entryPrice - value) / entryPrice) * 100, - ), - Match.when( - { side: "short", tpOrSl: "stopLoss" }, - () => ((value - entryPrice) / entryPrice) * 100, - ), - Match.when( - { side: "long", tpOrSl: "takeProfit" }, - () => ((value - entryPrice) / entryPrice) * 100, - ), - Match.when( - { side: "long", tpOrSl: "stopLoss" }, - () => ((entryPrice - value) / entryPrice) * 100, - ), - Match.exhaustive, - ), - ), - ); - const option = Match.value(percentage).pipe( - Match.when(null, () => null), - Match.when(0, () => null), - Match.orElse((value) => findMatchingOption(value)), - ); - - return { - option, - triggerPrice: amount || null, - percentage, - }; -}; diff --git a/src/components/ui/button.tsx b/packages/common/src/components/ui/button.tsx similarity index 76% rename from src/components/ui/button.tsx rename to packages/common/src/components/ui/button.tsx index 5be28e0..c3909df 100644 --- a/src/components/ui/button.tsx +++ b/packages/common/src/components/ui/button.tsx @@ -1,11 +1,10 @@ import { Button as ButtonPrimitive } from "@base-ui/react/button"; import { cva, type VariantProps } from "class-variance-authority"; - -import { cn } from "@/lib/utils"; +import { cn } from "../../lib/utils"; import { Spinner } from "./spinner"; const buttonVariants = cva( - "cursor-pointer focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-2xl border border-transparent bg-clip-padding text-sm focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none font-semibold text-base", + "cursor-pointer focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-xl border border-transparent bg-clip-padding text-sm focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none font-semibold text-base", { variants: { variant: { @@ -23,11 +22,11 @@ const buttonVariants = cva( link: "text-primary underline-offset-4 hover:underline", }, size: { + lg: "h-14 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3", default: - "h-15 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", - sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5", - lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3", + "h-12 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + sm: "h-10 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5", + xs: "h-8 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", icon: "size-9", "icon-xs": "size-6 rounded-full in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3", diff --git a/src/components/ui/card.tsx b/packages/common/src/components/ui/card.tsx similarity index 87% rename from src/components/ui/card.tsx rename to packages/common/src/components/ui/card.tsx index 1fc6d43..052a0b1 100644 --- a/src/components/ui/card.tsx +++ b/packages/common/src/components/ui/card.tsx @@ -1,6 +1,5 @@ import { cva, type VariantProps } from "class-variance-authority"; - -import { cn } from "@/lib/utils"; +import { cn } from "../../lib/utils"; const cardVariants = cva("flex flex-col w-full"); @@ -14,8 +13,8 @@ const cardSectionVariants = cva("bg-gray-3 px-4 py-[18px]", { variants: { position: { first: "rounded-t-2xl", - middle: "border-t border-[#090909]", - last: "rounded-b-2xl border-t border-[#090909]", + middle: "border-t border-surface-3", + last: "rounded-b-2xl border-t border-surface-3", only: "rounded-2xl", }, }, diff --git a/src/components/ui/dialog.tsx b/packages/common/src/components/ui/dialog.tsx similarity index 96% rename from src/components/ui/dialog.tsx rename to packages/common/src/components/ui/dialog.tsx index 38b1fed..5dca9cf 100644 --- a/src/components/ui/dialog.tsx +++ b/packages/common/src/components/ui/dialog.tsx @@ -2,8 +2,8 @@ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"; import { cva, type VariantProps } from "class-variance-authority"; import { X } from "lucide-react"; import type { ComponentProps } from "react"; -import { useRootContainer } from "@/context/root-container"; -import { cn } from "@/lib/utils"; +import { useRootContainer } from "../../context/root-container"; +import { cn } from "../../lib/utils"; // Re-export the Root component const Root = DialogPrimitive.Root; @@ -46,7 +46,7 @@ function Backdrop({ const dialogPopupVariants = cva( [ "fixed z-50", - "bg-[#222] rounded-2xl overflow-hidden", + "bg-[#151515] rounded-2xl overflow-hidden", "animate-in fade-in zoom-in-95 duration-200", "data-[ending-style]:opacity-0 data-[ending-style]:scale-95", "data-[starting-style]:opacity-0 data-[starting-style]:scale-95", diff --git a/packages/common/src/components/ui/divider.tsx b/packages/common/src/components/ui/divider.tsx new file mode 100644 index 0000000..ba86f00 --- /dev/null +++ b/packages/common/src/components/ui/divider.tsx @@ -0,0 +1,3 @@ +export const Divider = () => { + return
; +}; diff --git a/packages/common/src/components/ui/popover.tsx b/packages/common/src/components/ui/popover.tsx new file mode 100644 index 0000000..28a092f --- /dev/null +++ b/packages/common/src/components/ui/popover.tsx @@ -0,0 +1,128 @@ +import { Popover as PopoverPrimitive } from "@base-ui/react/popover"; +import type { ComponentProps } from "react"; +import { useRootContainer } from "../../context/root-container"; +import { cn } from "../../lib/utils"; + +// Re-export the Root component +const Root = PopoverPrimitive.Root; + +// Re-export the Trigger component +const Trigger = PopoverPrimitive.Trigger; + +// Portal with container support +const Portal = ({ + children, + ...props +}: ComponentProps) => { + const rootContainer = useRootContainer(); + + return ( + + {children} + + ); +}; + +// Positioner for positioning the popup +function Positioner({ + className, + sideOffset = 8, + ...props +}: ComponentProps) { + return ( + + ); +} + +// Popup with default styles +function Popup({ + className, + ...props +}: ComponentProps) { + return ( + + ); +} + +// Arrow component +function Arrow({ + className, + ...props +}: ComponentProps) { + return ( + + ); +} + +// Title with default styles +function Title({ + className, + ...props +}: ComponentProps) { + return ( + + ); +} + +// Description with default styles +function Description({ + className, + ...props +}: ComponentProps) { + return ( + + ); +} + +// Close button +function Close({ + className, + ...props +}: ComponentProps) { + return ; +} + +export const Popover = { + Root, + Trigger, + Portal, + Positioner, + Popup, + Arrow, + Title, + Description, + Close, +}; diff --git a/packages/common/src/components/ui/select.tsx b/packages/common/src/components/ui/select.tsx new file mode 100644 index 0000000..2bf40cf --- /dev/null +++ b/packages/common/src/components/ui/select.tsx @@ -0,0 +1,213 @@ +import { Select as SelectPrimitive } from "@base-ui/react/select"; +import { cva, type VariantProps } from "class-variance-authority"; +import { Check, ChevronDown } from "lucide-react"; +import type { ComponentProps, ReactNode } from "react"; +import { useRootContainer } from "../../context/root-container"; +import { cn } from "../../lib/utils"; + +// Re-export the Root component +const Root = SelectPrimitive.Root; + +// Re-export the Value component +const Value = SelectPrimitive.Value; + +// Re-export the Icon component +const Icon = SelectPrimitive.Icon; + +// Re-export the Group component +const Group = SelectPrimitive.Group; + +// Re-export the GroupLabel component +const GroupLabel = ({ + className, + ...props +}: ComponentProps) => ( + +); + +const selectTriggerVariants = cva( + [ + "flex items-center justify-between gap-2", + "bg-white/5 rounded-lg", + "text-white text-[13px]", + "transition-colors", + "hover:bg-white/10", + "focus:outline-none", + "disabled:opacity-50 disabled:cursor-not-allowed", + ], + { + variants: { + size: { + sm: "h-8 px-3", + default: "h-10 px-3.5", + lg: "h-12 px-4", + }, + }, + defaultVariants: { + size: "default", + }, + }, +); + +interface TriggerProps + extends ComponentProps, + VariantProps {} + +function Trigger({ className, size, children, ...props }: TriggerProps) { + return ( + + {children} + + + + + ); +} + +// Portal with container support +const Portal = ({ + children, + ...props +}: ComponentProps) => { + const rootContainer = useRootContainer(); + + return ( + + {children} + + ); +}; + +// Positioner for positioning the popup +function Positioner({ + className, + ...props +}: ComponentProps) { + return ( + + ); +} + +// Popup with default styles +function Popup({ + className, + ...props +}: ComponentProps) { + return ( + + ); +} + +// Item with default styles +interface ItemProps extends ComponentProps { + icon?: ReactNode; + showIndicator?: boolean; +} + +function Item({ + className, + icon, + children, + showIndicator = true, + ...props +}: ItemProps) { + return ( + + {icon && {icon}} + + {children} + + {showIndicator && ( + + + + )} + + ); +} + +// ItemText component +const ItemText = ({ + className, + ...props +}: ComponentProps) => ( + +); + +// ItemIndicator component +const ItemIndicator = ({ + className, + children, + ...props +}: ComponentProps) => ( + + {children ?? } + +); + +// Separator +function Separator({ + className, + ...props +}: ComponentProps) { + return ( + + ); +} + +export const Select = { + Root, + Trigger, + Value, + Icon, + Portal, + Positioner, + Popup, + Group, + GroupLabel, + Item, + ItemText, + ItemIndicator, + Separator, +}; diff --git a/src/components/ui/skeleton.tsx b/packages/common/src/components/ui/skeleton.tsx similarity index 96% rename from src/components/ui/skeleton.tsx rename to packages/common/src/components/ui/skeleton.tsx index 29becb8..96fd951 100644 --- a/src/components/ui/skeleton.tsx +++ b/packages/common/src/components/ui/skeleton.tsx @@ -1,4 +1,4 @@ -import { cn } from "@/lib/utils"; +import { cn } from "../../lib/utils"; interface SkeletonProps { className?: string; diff --git a/src/components/ui/slider.tsx b/packages/common/src/components/ui/slider.tsx similarity index 99% rename from src/components/ui/slider.tsx rename to packages/common/src/components/ui/slider.tsx index a3b1305..4a8a1f4 100644 --- a/src/components/ui/slider.tsx +++ b/packages/common/src/components/ui/slider.tsx @@ -2,7 +2,7 @@ import { Slider as SliderPrimitive } from "@base-ui/react/slider"; import { cva, type VariantProps } from "class-variance-authority"; import type { ReactNode } from "react"; import React from "react"; -import { cn } from "@/lib/utils"; +import { cn } from "../../lib/utils"; const sliderControlVariants = cva( "relative flex touch-none select-none items-center", diff --git a/src/components/ui/spinner.tsx b/packages/common/src/components/ui/spinner.tsx similarity index 94% rename from src/components/ui/spinner.tsx rename to packages/common/src/components/ui/spinner.tsx index 8149f6a..c4e04c4 100644 --- a/src/components/ui/spinner.tsx +++ b/packages/common/src/components/ui/spinner.tsx @@ -1,4 +1,4 @@ -import { cn } from "@/lib/utils"; +import { cn } from "../../lib/utils"; function Spinner({ className }: { className?: string }) { return ( diff --git a/src/components/ui/tabs.tsx b/packages/common/src/components/ui/tabs.tsx similarity index 95% rename from src/components/ui/tabs.tsx rename to packages/common/src/components/ui/tabs.tsx index 72f9209..86ef6f8 100644 --- a/src/components/ui/tabs.tsx +++ b/packages/common/src/components/ui/tabs.tsx @@ -1,7 +1,6 @@ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"; import { cva, type VariantProps } from "class-variance-authority"; - -import { cn } from "@/lib/utils"; +import { cn } from "../../lib/utils"; function Tabs({ className, @@ -30,7 +29,7 @@ const tabsListVariants = cva( line: "gap-2.5 bg-transparent", pill: "gap-1 bg-transparent p-0 h-auto", contained: - "bg-[#121314] h-12 p-[5px] rounded-[10px] gap-[5px] w-full overflow-hidden", + "bg-surface-2 h-12 p-[5px] rounded-[10px] gap-[5px] w-full overflow-hidden", }, }, defaultVariants: { @@ -62,7 +61,7 @@ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) { // Base styles "relative inline-flex h-9 flex-1 items-center justify-center whitespace-nowrap rounded-xl px-4 py-2 text-sm font-semibold transition-all", // Default inactive state - dark background - "bg-[#121314] text-muted-foreground", + "bg-surface-2 text-muted-foreground", // Active state - white background, black text "data-active:bg-white data-active:text-black data-active:border data-active:border-white", // Pill variant styling via parent diff --git a/packages/common/src/components/ui/text.tsx b/packages/common/src/components/ui/text.tsx new file mode 100644 index 0000000..80ab79a --- /dev/null +++ b/packages/common/src/components/ui/text.tsx @@ -0,0 +1,100 @@ +import { cva, type VariantProps } from "class-variance-authority"; +import type { HTMLAttributes } from "react"; +import { cn } from "../../lib/utils"; + +export const textVariants = cva("", { + variants: { + variant: { + inherit: "", + titleXl: "font-semibold text-xl text-foreground", + titleXlTight: "font-semibold text-xl text-foreground tracking-[-0.6px]", + titleLg: + "text-foreground text-lg font-semibold leading-tight tracking-[-0.72px]", + titleMd: + "text-foreground text-md font-semibold leading-tight tracking-[-0.54px]", + bodyMd: "text-md", + headingBase: "text-foreground font-semibold text-base", + headingBaseTight: + "text-foreground font-semibold text-base tracking-tight", + labelBaseWhite: "text-white font-semibold text-base tracking-tight", + labelBase: "font-semibold text-base tracking-tight", + labelSm: "font-semibold text-sm tracking-tight", + labelSmSemibold: "text-sm font-semibold", + labelBaseSemibold: "text-base font-semibold", + labelXs: "font-semibold text-xs tracking-tight", + labelSmWhiteNeg: + "font-semibold text-sm text-white tracking-[-0.42px] leading-tight", + labelSmWhiteNegNoLeading: + "font-semibold text-sm text-white tracking-[-0.42px]", + labelSmForegroundNeg: + "font-semibold text-sm text-foreground tracking-[-0.42px] leading-tight", + bodySmWhiteNeg: + "text-sm text-white font-normal tracking-[-0.42px] leading-tight", + bodySmWhiteNegNoLeading: + "text-white text-sm font-normal tracking-[-0.42px]", + bodySmWhite: "text-white text-sm font-normal", + labelSmGray2Tight: "text-gray-2 font-semibold text-sm tracking-tight", + labelSmGray2Neg: "text-gray-2 text-sm font-semibold tracking-[-0.42px]", + labelSmGray2NegTight: + "text-gray-2 font-semibold text-sm leading-tight tracking-[-0.42px]", + bodySmGray2: "text-sm text-gray-2", + bodySmMedium: "text-sm font-medium", + bodySmGray2Tight: "text-gray-2 text-sm font-normal tracking-tight", + bodySmTight: "text-sm font-normal tracking-tight", + bodySmGray2Neg: "text-gray-2 text-sm font-normal tracking-[-0.42px]", + bodySmGray2NegTight: + "text-gray-2 font-normal text-sm tracking-[-0.42px] leading-tight", + bodySmNeg: "text-sm font-normal tracking-[-0.42px]", + bodyXsGray2: "text-xs text-gray-2", + labelXsGray2: "text-gray-2 font-semibold text-xs tracking-tight", + labelXsWhiteNeg: "text-white text-xs font-semibold tracking-[-0.24px]", + helperSmGray1: "text-gray-1 text-sm", + helperSmGray1Tight: "text-gray-1 text-sm tracking-tight", + bodyXsGray2Medium: "font-medium text-gray-2 text-xs", + bodySmForegroundMedium: "font-medium text-foreground text-sm", + bodyBaseForegroundMedium: "font-medium text-foreground", + bodyBaseForegroundMediumTight: + "font-medium text-foreground tracking-tight", + caption11Gray2Medium: "font-medium text-[11px] text-gray-2", + amountDisplay30: + "text-white font-semibold text-[30px] tracking-tight leading-tight", + amountDisplay44: + "text-white text-[44px] font-semibold tracking-[-1.76px] leading-none", + optionTitle: + "text-white font-bold text-base tracking-[-0.48px] leading-tight", + badgePrimary: + "px-2.5 py-1 text-xs font-semibold rounded-full bg-primary/10 text-primary", + badgeDestructive: + "px-2.5 py-1 text-xs font-semibold rounded-full bg-destructive/10 text-destructive", + badgeNeutral: + "px-2.5 py-1 text-xs font-semibold rounded-full bg-gray-3 text-gray-2", + badgeWhiteSmall: + "bg-white/25 px-1.5 py-1 rounded-[5px] text-[11px] text-white leading-none", + badgeWhiteTight: + "bg-white/25 px-1.5 py-1 rounded text-[11px] text-white text-center leading-tight", + badgeSideTight: + "px-1.5 py-1 rounded text-[11px] text-white text-center leading-tight", + }, + }, + defaultVariants: { + variant: "bodySmGray2", + }, +}); + +type TextAs = "span" | "p" | "div" | "label" | "h1" | "h2" | "h3"; + +export type TextProps = HTMLAttributes & + VariantProps & { + as?: TextAs; + }; + +export function Text({ as = "span", variant, className, ...props }: TextProps) { + const Comp = as; + return ( + + ); +} diff --git a/src/components/ui/toaster.tsx b/packages/common/src/components/ui/toaster.tsx similarity index 100% rename from src/components/ui/toaster.tsx rename to packages/common/src/components/ui/toaster.tsx diff --git a/src/context/appkit.tsx b/packages/common/src/context/appkit.tsx similarity index 87% rename from src/context/appkit.tsx rename to packages/common/src/context/appkit.tsx index 488247d..5dfd993 100644 --- a/src/context/appkit.tsx +++ b/packages/common/src/context/appkit.tsx @@ -1,8 +1,8 @@ import { Result, useAtomValue } from "@effect-atom/atom-react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { WagmiProvider } from "wagmi"; -import { configAtom } from "@/atoms/config-atom"; -import { walletAtom } from "@/atoms/wallet-atom"; +import { configAtom } from "../atoms/config-atom"; +import { walletAtom } from "../atoms/wallet-atom"; const qc = new QueryClient(); diff --git a/src/context/index.tsx b/packages/common/src/context/index.tsx similarity index 62% rename from src/context/index.tsx rename to packages/common/src/context/index.tsx index b6ef508..8105a5f 100644 --- a/src/context/index.tsx +++ b/packages/common/src/context/index.tsx @@ -1,7 +1,7 @@ import { StrictMode } from "react"; -import { Toaster } from "@/components/ui/toaster"; -import { AppKit } from "@/context/appkit"; -import { RootContainerProvider } from "@/context/root-container"; +import { Toaster } from "../components/ui/toaster"; +import { AppKit } from "./appkit"; +import { RootContainerProvider } from "./root-container"; export const Providers = ({ children }: { children: React.ReactNode }) => { return ( @@ -15,3 +15,5 @@ export const Providers = ({ children }: { children: React.ReactNode }) => { ); }; + +export * from "./root-container"; diff --git a/src/context/root-container.tsx b/packages/common/src/context/root-container.tsx similarity index 100% rename from src/context/root-container.tsx rename to packages/common/src/context/root-container.tsx diff --git a/src/domain/chains/evm.ts b/packages/common/src/domain/chains/evm.ts similarity index 100% rename from src/domain/chains/evm.ts rename to packages/common/src/domain/chains/evm.ts diff --git a/packages/common/src/domain/chains/index.ts b/packages/common/src/domain/chains/index.ts new file mode 100644 index 0000000..22d91bc --- /dev/null +++ b/packages/common/src/domain/chains/index.ts @@ -0,0 +1,6 @@ +import type { SupportedEvmChain } from "./evm"; + +export type SupportedSKChains = SupportedEvmChain; + +export * from "./evm"; +export * from "./ledger"; diff --git a/src/domain/chains/ledger.ts b/packages/common/src/domain/chains/ledger.ts similarity index 95% rename from src/domain/chains/ledger.ts rename to packages/common/src/domain/chains/ledger.ts index cfb98e9..a2f53d6 100644 --- a/src/domain/chains/ledger.ts +++ b/packages/common/src/domain/chains/ledger.ts @@ -1,6 +1,6 @@ import type { Currency, Families } from "@ledgerhq/wallet-api-client"; import { EvmNetworks } from "@stakekit/common"; -import type { SupportedSKChains } from "@/domain/chains"; +import type { SupportedSKChains } from "./index"; export type SupportedLedgerLiveFamilies = Extract; diff --git a/packages/common/src/domain/index.ts b/packages/common/src/domain/index.ts new file mode 100644 index 0000000..a8bc10d --- /dev/null +++ b/packages/common/src/domain/index.ts @@ -0,0 +1,6 @@ +export * from "./chains"; +export * from "./signer"; +export * from "./tokens"; +export * from "./transactions"; +export * from "./types"; +export * from "./wallet"; diff --git a/src/domain/signer.ts b/packages/common/src/domain/signer.ts similarity index 96% rename from src/domain/signer.ts rename to packages/common/src/domain/signer.ts index 3a01379..8d204a7 100644 --- a/src/domain/signer.ts +++ b/packages/common/src/domain/signer.ts @@ -1,6 +1,6 @@ import type { WagmiAdapter } from "@reown/appkit-adapter-wagmi"; import { Data, type Effect, Schema, type Stream } from "effect"; -import type { Transaction, TransactionHash } from "@/domain/transactions"; +import type { Transaction, TransactionHash } from "./transactions"; const BrowserWalletAccount = Schema.Struct({ address: Schema.String.pipe(Schema.brand("WalletAccountAddress")), diff --git a/src/domain/tokens.ts b/packages/common/src/domain/tokens.ts similarity index 92% rename from src/domain/tokens.ts rename to packages/common/src/domain/tokens.ts index 0f75b60..a0a8b96 100644 --- a/src/domain/tokens.ts +++ b/packages/common/src/domain/tokens.ts @@ -1,5 +1,5 @@ import { Equal, Schema } from "effect"; -import { Networks } from "@/services/api-client/api-schemas"; +import { Networks } from "../services/api-client/api-schemas"; const Token = Schema.Struct({ network: Networks, diff --git a/src/domain/transactions.ts b/packages/common/src/domain/transactions.ts similarity index 95% rename from src/domain/transactions.ts rename to packages/common/src/domain/transactions.ts index b72475d..82dcd60 100644 --- a/src/domain/transactions.ts +++ b/packages/common/src/domain/transactions.ts @@ -9,6 +9,7 @@ export const EvmTx = Schema.Struct({ value: Schema.optional(Schema.BigInt), gasLimit: Schema.BigInt, chainId: Schema.Number, + nonce: Schema.optional(Schema.Number), }); export const EIP712Tx = Schema.Struct({ diff --git a/src/domain/types.ts b/packages/common/src/domain/types.ts similarity index 79% rename from src/domain/types.ts rename to packages/common/src/domain/types.ts index 8bc149d..226d817 100644 --- a/src/domain/types.ts +++ b/packages/common/src/domain/types.ts @@ -1,4 +1,4 @@ -import type { TokenDto } from "@/services/api-client/api-schemas"; +import type { TokenDto } from "../services/api-client/api-schemas"; export type TokenString = `${TokenDto["network"]}-${TokenDto["address"]}`; diff --git a/src/domain/wallet.ts b/packages/common/src/domain/wallet.ts similarity index 94% rename from src/domain/wallet.ts rename to packages/common/src/domain/wallet.ts index 0c2425b..5064c34 100644 --- a/src/domain/wallet.ts +++ b/packages/common/src/domain/wallet.ts @@ -2,6 +2,11 @@ import type { HttpClientError } from "@effect/platform"; import type { WagmiAdapter } from "@reown/appkit-adapter-wagmi"; import { Data, type Effect, type Stream } from "effect"; import type { ParseError } from "effect/ParseResult"; +import type { + ActionDto, + TransactionDto, +} from "../services/api-client/api-schemas"; +import type { SKClientError } from "../services/api-client/client-factory"; import type { BrowserSigner, BrowserWalletAccount, @@ -10,12 +15,7 @@ import type { SignTransactionError, SwitchAccountError, WalletAccount, -} from "@/domain/signer"; -import type { - ActionDto, - TransactionDto, -} from "@/services/api-client/api-schemas"; -import type { SKClientError } from "@/services/api-client/client-factory"; +} from "./signer"; export class TransactionNotConfirmedError extends Data.TaggedError( "TransactionNotConfirmedError", @@ -118,4 +118,4 @@ export const isLedgerWalletConnected = ( ): wallet is LedgerWalletConnected => isWalletConnected(wallet) && wallet.type === "ledger"; -export type { WalletAccount } from "@/domain/signer"; +export type { WalletAccount } from "./signer"; diff --git a/packages/common/src/hooks/index.ts b/packages/common/src/hooks/index.ts new file mode 100644 index 0000000..6661bfa --- /dev/null +++ b/packages/common/src/hooks/index.ts @@ -0,0 +1,3 @@ +export * from "./use-order-actions"; +export * from "./use-position-actions"; +export * from "./use-tp-sl-orders"; diff --git a/src/hooks/use-order-actions.ts b/packages/common/src/hooks/use-order-actions.ts similarity index 91% rename from src/hooks/use-order-actions.ts rename to packages/common/src/hooks/use-order-actions.ts index ebddc18..ce66819 100644 --- a/src/hooks/use-order-actions.ts +++ b/packages/common/src/hooks/use-order-actions.ts @@ -1,5 +1,5 @@ import { Option, Schema } from "effect"; -import type { OrderDto } from "@/services/api-client/api-schemas"; +import type { OrderDto } from "../services/api-client/api-schemas"; const CancelOrderPendingActionSchema = Schema.Struct({ type: Schema.Literal("cancelOrder"), diff --git a/src/hooks/use-position-actions.ts b/packages/common/src/hooks/use-position-actions.ts similarity index 93% rename from src/hooks/use-position-actions.ts rename to packages/common/src/hooks/use-position-actions.ts index e84c9ed..7180101 100644 --- a/src/hooks/use-position-actions.ts +++ b/packages/common/src/hooks/use-position-actions.ts @@ -1,11 +1,10 @@ import { Match, Option, Schema } from "effect"; -import type { PositionDto } from "@/services/api-client/api-schemas"; +import type { PositionDto } from "../services/api-client/api-schemas"; const UpdateLeverageSchema = Schema.Struct({ type: Schema.Literal("updateLeverage"), args: Schema.Struct({ marketId: Schema.String, - leverage: Schema.Number, }), }); diff --git a/src/hooks/use-tp-sl-orders.ts b/packages/common/src/hooks/use-tp-sl-orders.ts similarity index 93% rename from src/hooks/use-tp-sl-orders.ts rename to packages/common/src/hooks/use-tp-sl-orders.ts index 158a4de..aabd670 100644 --- a/src/hooks/use-tp-sl-orders.ts +++ b/packages/common/src/hooks/use-tp-sl-orders.ts @@ -1,5 +1,5 @@ import { Match, Option, Schema } from "effect"; -import type { OrderDto } from "@/services/api-client/api-schemas"; +import type { OrderDto } from "../services/api-client/api-schemas"; const TpSlOrderSchema = Schema.Struct({ type: Schema.Literal("take_profit", "stop_loss"), diff --git a/packages/common/src/lib/formatting.ts b/packages/common/src/lib/formatting.ts new file mode 100644 index 0000000..eb1517d --- /dev/null +++ b/packages/common/src/lib/formatting.ts @@ -0,0 +1,163 @@ +/** + * Formatting helpers (display-only). + * + * Keep this module free of business logic / calculations; those live in `lib/math.ts`. + */ + +import { Option } from "effect"; +import type { TPOrSLSettings } from "../components/molecules/tp-sl-dialog"; + +const tokenAmountFormatter = new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 5, +}); + +/** + * Formats a token amount as `"SYMBOL 1.2345"`. + */ +export const formatTokenAmount = ({ + amount, + symbol, +}: { + amount: number | string; + symbol: string; +}) => { + const parsedAmount = typeof amount === "string" ? parseFloat(amount) : amount; + return `${symbol} ${tokenAmountFormatter.format(parsedAmount)}`; +}; + +/** + * Formats a USD-like amount with adaptive decimals and optional sign. + * + * This is used widely across the widget UI for prices, balances, margin, PnL, etc. + * + * Negative values are formatted as `-$37.1` (minus before symbol). + */ +export const formatAmount = ( + amount: number | string, + options = { symbol: "$" } as { + minimumFractionDigits?: number; + maximumFractionDigits?: number; + withSign?: boolean; + symbol?: string | null; + }, +): string => { + const amountNumber = typeof amount === "string" ? parseFloat(amount) : amount; + const symbol = options.symbol !== null ? (options.symbol ?? "$") : ""; + const isNegative = amountNumber < 0; + const absAmount = Math.abs(amountNumber); + + const hasCustomDigits = + options.minimumFractionDigits !== undefined || + options.maximumFractionDigits !== undefined; + + const { maxDigits, minDigits } = (() => { + if (hasCustomDigits) { + return { + minDigits: options.minimumFractionDigits, + maxDigits: options.maximumFractionDigits, + }; + } + + if (absAmount >= 1000) { + return { minDigits: undefined, maxDigits: 0 }; + } + if (absAmount >= 1) { + return { minDigits: 2, maxDigits: 4 }; + } + if (absAmount >= 0.01) { + return { minDigits: undefined, maxDigits: 4 }; + } + if (absAmount === 0) { + return { minDigits: 2, maxDigits: undefined }; + } + return { minDigits: 6, maxDigits: undefined }; + })(); + + const formattedNumber = absAmount.toLocaleString("en-US", { + minimumFractionDigits: minDigits, + maximumFractionDigits: maxDigits, + }); + + const sign = options?.withSign + ? isNegative + ? "-" + : "+" + : isNegative + ? "-" + : ""; + + return `${sign}${symbol}${formattedNumber}`; +}; + +/** + * Formats a percentage number: `12.345` -> `"12.35%"` + */ +export const formatPercentage = ( + percentage: number, + options?: { + maximumFractionDigits?: number; + }, +) => `${percentage.toFixed(options?.maximumFractionDigits ?? 2)}%`; + +/** + * Formats a rate string as a percentage. + * + * Example: `"0.0001"` -> `"0.01%"` + */ +export const formatRate = ( + rate: string, + options?: { + maximumFractionDigits?: number; + }, +) => formatPercentage(Number.parseFloat(rate) * 100, options); + +/** + * Returns a human-friendly USD amount using compact suffixes (K/M/B). + * + * - `1234` -> `$1.23K` + * - `2_500_000` -> `$2.50M` + * + * For smaller values it falls back to `formatAmount`. + */ +export function formatCompactUsdAmount(volume: number): string { + if (volume >= 1_000_000_000) { + return `$${(volume / 1_000_000_000).toFixed(2)}B`; + } + if (volume >= 1_000_000) { + return `$${(volume / 1_000_000).toFixed(2)}M`; + } + if (volume >= 1_000) { + return `$${(volume / 1_000).toFixed(2)}K`; + } + return formatAmount(volume); +} + +/** + * Formats TP/SL settings for display. + * + * - Long: "TP +10%, SL -5%" or "TP Off, SL Off" + * - Short: "TP -10%, SL +5%" or "TP Off, SL Off" + */ +export function formatTPOrSLSettings( + settings: TPOrSLSettings, + side: "long" | "short" = "long", +): string { + const tp = Option.fromNullable(settings.takeProfit.percentage).pipe( + Option.filter((percentage) => percentage !== 0), + Option.map((percentage) => + side === "short" ? `TP -${percentage}%` : `TP +${percentage}%`, + ), + Option.getOrElse(() => "TP Off"), + ); + + const sl = Option.fromNullable(settings.stopLoss.percentage).pipe( + Option.filter((percentage) => percentage !== 0), + Option.map((percentage) => + side === "short" ? `SL +${percentage}%` : `SL -${percentage}%`, + ), + Option.getOrElse(() => "SL Off"), + ); + + return `${tp}, ${sl}`; +} diff --git a/packages/common/src/lib/index.ts b/packages/common/src/lib/index.ts new file mode 100644 index 0000000..ade722f --- /dev/null +++ b/packages/common/src/lib/index.ts @@ -0,0 +1,3 @@ +export * from "./formatting"; +export * from "./math"; +export * from "./utils"; diff --git a/packages/common/src/lib/math.ts b/packages/common/src/lib/math.ts new file mode 100644 index 0000000..cda7017 --- /dev/null +++ b/packages/common/src/lib/math.ts @@ -0,0 +1,486 @@ +import { Number as _Number, Option } from "effect"; +import type { + MarketDto, + PositionDto, +} from "../services/api-client/api-schemas"; + +/** + * The minimum leverage supported by the UI/SDK. + * + * Note: this was previously defined in `domain/market.ts`, but lives here since + * it is tightly coupled to leverage-related math helpers. + */ +export const MIN_LEVERAGE = 1; + +/** + * A reasonable default max leverage fallback. + * + * Note: some markets provide their own leverage range; use `getMaxLeverage`. + */ +export const MAX_LEVERAGE = 40; + +/** + * Safely parses a decimal string to a number. + * + * Returns `fallback` when parsing fails. + */ +export const parseFloatOr = (value: string, fallback: number): number => { + return _Number.parse(value).pipe(Option.getOrElse(() => fallback)); +}; + +/** + * Safely parses a decimal string to a number. + * + * Returns `0` when parsing fails. + */ +export const parseFloatOrZero = (value: string): number => + parseFloatOr(value, 0); + +/** + * Clamps a percentage into the \([0, 100]\) interval. + */ +export const clampPercent = (percent: number): number => + _Number.clamp({ minimum: 0, maximum: 100 })(percent); + +/** + * Computes \( \frac{part}{whole} \cdot 100 \) with safe handling for `whole <= 0`. + */ +export const percentOf = ({ + part, + whole, +}: { + part: number; + whole: number; +}): number => { + if (whole <= 0) return 0; + return (part / whole) * 100; +}; + +/** + * Computes `total * percent/100`. + */ +export const valueFromPercent = ({ + total, + percent, +}: { + total: number; + percent: number; +}): number => total * (percent / 100); + +/** + * Applies a percentage delta to a value: `value * (1 + percent/100)`. + * + * This is commonly used for "quick adjust" UI controls (e.g. `-1%`, `-5%`). + */ +export const applyPercentDelta = ({ + value, + percent, +}: { + value: number; + percent: number; +}): number => value * (1 + percent / 100); + +/** + * Estimates the 24h low/high from a current price and an absolute change magnitude. + * + * This is useful when the backend provides `priceChange24h` but not the explicit low/high. + */ +export const estimateLowHighFromAbsoluteChange = ({ + currentPrice, + priceChange24h, +}: { + currentPrice: number; + priceChange24h: number; +}): { low: number; high: number } => { + const d = Math.abs(priceChange24h); + return { low: currentPrice - d, high: currentPrice + d }; +}; + +/** + * Computes the notional value (USD) given a price and size. + * + * `sizeBase` can be either a number or a decimal string coming from DTOs. + */ +export const calcNotionalUsd = ({ + priceUsd, + sizeBase, +}: { + priceUsd: number; + sizeBase: number | string; +}): number => { + const s = + typeof sizeBase === "string" ? parseFloatOrZero(sizeBase) : sizeBase; + return priceUsd * s; +}; + +/** + * Computes PnL% = (pnl / margin) * 100 with safe handling for `margin <= 0`. + */ +export const calcPnlPercent = ({ + pnlUsd, + marginUsd, +}: { + pnlUsd: number; + marginUsd: number; +}): number => (marginUsd > 0 ? (pnlUsd / marginUsd) * 100 : 0); + +/** + * Converts a USD amount into a crypto/base amount using a USD price. + */ +export const calcBaseAmountFromUsd = ({ + usdAmount, + priceUsd, +}: { + usdAmount: number; + priceUsd: number; +}): number => { + if (priceUsd <= 0) return 0; + return usdAmount / priceUsd; +}; + +/** + * Converts a crypto/base amount into USD using a USD price. + */ +export const calcUsdFromBaseAmount = ({ + baseAmount, + priceUsd, +}: { + baseAmount: number; + priceUsd: number; +}): number => baseAmount * priceUsd; + +/** + * Truncates a number down to the given decimal precision. + * + * Example: `round(1.239, 2) -> 1.23` + */ +export const round = (number: number, precision: number = 2) => + _Number.round(number, precision); + +/** + * Computes max leverage from the leverage range (falls back to `MAX_LEVERAGE`). + */ +export const getMaxLeverage = ( + leverages: MarketDto["leverageRange"], +): number => + leverages.length > 0 + ? (leverages[leverages.length - 1] ?? MAX_LEVERAGE) + : MAX_LEVERAGE; + +/** + * Generic percent change between two prices: \(\frac{current - reference}{reference} \cdot 100\). + */ +export const getPriceChangePercent = ({ + currentPrice, + pastOrFuturePrice, +}: { + currentPrice: number; + pastOrFuturePrice: number; +}) => { + if (pastOrFuturePrice === 0) return 100; + return ((currentPrice - pastOrFuturePrice) / pastOrFuturePrice) * 100; +}; + +/** + * Price change percent for a position relative to entry. + */ +export const getPositionChangePercent = (position: PositionDto) => + getPriceChangePercent({ + currentPrice: position.markPrice, + pastOrFuturePrice: position.entryPrice, + }); + +/** + * Calculates the liquidation price given a current price, leverage and side. + */ +export const getLiquidationPrice = ({ + currentPrice, + leverage, + side, +}: { + currentPrice: number; + leverage: number; + side: "long" | "short"; +}) => + side === "long" + ? currentPrice * (1 - 1 / leverage) + : currentPrice * (1 + 1 / leverage); + +/** + * Percent price change needed to reach liquidation. + */ +export const getPriceChangePercentToLiquidation = ({ + currentPrice, + liquidationPrice, +}: { + currentPrice: number; + liquidationPrice: number; +}) => + getPriceChangePercent({ + currentPrice, + pastOrFuturePrice: liquidationPrice, + }); + +/** + * Maps a leverage into slider percent space between `MIN_LEVERAGE` and `maxLeverage`. + */ +export const getLeveragePercent = ({ + leverage, + maxLeverage, +}: { + leverage: number; + maxLeverage: number; +}) => ((leverage - MIN_LEVERAGE) / (maxLeverage - MIN_LEVERAGE)) * 100; + +/** + * Converts a slider percent \([0..100]\) into a leverage value between + * `MIN_LEVERAGE` and `maxLeverage`. + */ +export const getLeverageFromPercent = ({ + percent, + maxLeverage, +}: { + percent: number; + maxLeverage: number; +}): number => { + const p = clampPercent(percent); + return Math.round(MIN_LEVERAGE + (p / 100) * (maxLeverage - MIN_LEVERAGE)); +}; + +/** + * Recommended leverage slider "stop" markers. + */ +export const getLeverageStops = (maxLeverage: number): number[] => [ + MIN_LEVERAGE, + Math.round(maxLeverage / 2), + maxLeverage, +]; + +/** + * Generates common leverage button presets (2x, 5x, 10x, 20x... up to max). + */ +export const generateLeverageButtons = (maxLeverage: number): number[] => { + const buttons: number[] = []; + + if (maxLeverage >= 2) { + buttons.push(2); + } + + if (maxLeverage >= 5) { + buttons.push(5); + } + + let value = 10; + while (value <= maxLeverage) { + buttons.push(value); + value *= 2; + } + + if (buttons[buttons.length - 1] !== maxLeverage && maxLeverage > 2) { + buttons.push(maxLeverage); + } + + return [...new Set(buttons)].sort((a, b) => a - b); +}; + +/** + * Calculates required margin for a notional position size and leverage. + */ +export const calculateMargin = ({ + positionSize, + leverage, +}: { + positionSize: number; + leverage: number; +}) => { + if (leverage <= 0) return positionSize; + return positionSize / leverage; +}; + +/** + * Calculates notional position size from margin and leverage. + */ +export const calculatePositionSize = ({ + margin, + leverage, +}: { + margin: number; + leverage: number; +}) => margin * leverage; + +/** + * Computes close-related UI calculations for a given close percentage. + */ +export const getCloseCalculations = ( + position: PositionDto, + closePercentage: number, +) => { + const ratio = closePercentage / 100; + const sizeNum = parseFloatOrZero(position.size); + const positionValue = position.markPrice * sizeNum; + const closeValue = positionValue * ratio; + const marginReturn = position.margin * ratio; + const pnlReturn = position.unrealizedPnl * ratio; + const youWillReceive = marginReturn + pnlReturn; + const closeSize = sizeNum * ratio; + const closeSizeInMarketPrice = round( + closeSize * position.markPrice, + 6, + ).toString(); + + return { + closeValue, + marginReturn, + pnlReturn, + youWillReceive, + closeSize, + closeSizeInMarketPrice, + }; +}; + +export type TpSlKind = "takeProfit" | "stopLoss"; + +/** + * Returns the "+" / "-" prefix used for TP/SL percent labels. + */ +export const getTpSlPercentPrefix = ({ + side, + tpOrSl, +}: { + side: "long" | "short"; + tpOrSl: TpSlKind; +}): "+" | "-" => { + if (side === "short") { + return tpOrSl === "takeProfit" ? "-" : "+"; + } + return tpOrSl === "takeProfit" ? "+" : "-"; +}; + +/** + * Calculates a TP/SL trigger price from entry and a (positive) percent move. + */ +export const calcTpSlTriggerPriceFromPercent = ({ + entryPrice, + percent, + side, + tpOrSl, +}: { + entryPrice: number; + percent: number; + side: "long" | "short"; + tpOrSl: TpSlKind; +}): number => { + const p = percent / 100; + const prefix = getTpSlPercentPrefix({ side, tpOrSl }); + return prefix === "+" ? entryPrice * (1 + p) : entryPrice * (1 - p); +}; + +/** + * Calculates the (positive) percent move between entry and trigger price for TP/SL. + */ +export const calcTpSlPercentFromTriggerPrice = ({ + entryPrice, + triggerPrice, + side, + tpOrSl, +}: { + entryPrice: number; + triggerPrice: number; + side: "long" | "short"; + tpOrSl: TpSlKind; +}): number => { + if (entryPrice <= 0) return 0; + const prefix = getTpSlPercentPrefix({ side, tpOrSl }); + const raw = + prefix === "+" + ? ((triggerPrice - entryPrice) / entryPrice) * 100 + : ((entryPrice - triggerPrice) / entryPrice) * 100; + return raw; +}; + +/** + * Default TP/SL percent presets used by the widget UI. + */ +export const DEFAULT_TP_SL_PERCENT_OPTIONS = [0, 10, 25, 50, 100] as const; +type DefaultTpSlPercentOption = (typeof DEFAULT_TP_SL_PERCENT_OPTIONS)[number]; + +/** + * Finds a matching percent option within a tolerance. + */ +export const findMatchingPercentOption = ({ + percent, + options, + tolerance = 0.5, +}: { + percent: number; + options: ReadonlyArray; + tolerance?: number; +}): T | null => + options.find((opt) => Math.abs(percent - opt) < tolerance) ?? null; + +/** + * Computes TP/SL configuration (percentage + option) from a trigger price. + * + * This is used to prefill UI when the backend provides an existing TP/SL order. + */ +export function getTPOrSLConfigurationFromPosition(args: { + entryPrice: number; + amount: number | undefined; + tpOrSl: TpSlKind; + side?: "long" | "short"; +}): { + option: DefaultTpSlPercentOption | null; + triggerPrice: number | null; + percentage: number | null; +}; +export function getTPOrSLConfigurationFromPosition(args: { + entryPrice: number; + amount: number | undefined; + tpOrSl: TpSlKind; + side?: "long" | "short"; + options: ReadonlyArray; +}): { + option: T | null; + triggerPrice: number | null; + percentage: number | null; +}; +export function getTPOrSLConfigurationFromPosition({ + amount, + entryPrice, + tpOrSl, + side = "long", + options, +}: { + entryPrice: number; + amount: number | undefined; + tpOrSl: TpSlKind; + side?: "long" | "short"; + options?: ReadonlyArray; +}): { + option: T | DefaultTpSlPercentOption | null; + triggerPrice: number | null; + percentage: number | null; +} { + const optList = + options ?? (DEFAULT_TP_SL_PERCENT_OPTIONS as unknown as ReadonlyArray); + const percentage = + amount === undefined + ? null + : calcTpSlPercentFromTriggerPrice({ + entryPrice, + triggerPrice: amount, + side, + tpOrSl, + }); + + const option = + percentage === null || percentage === 0 + ? null + : findMatchingPercentOption({ percent: percentage, options: optList }); + + return { + option, + triggerPrice: amount || null, + percentage, + }; +} diff --git a/packages/common/src/lib/utils.ts b/packages/common/src/lib/utils.ts new file mode 100644 index 0000000..5b71b14 --- /dev/null +++ b/packages/common/src/lib/utils.ts @@ -0,0 +1,40 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; +import type { TokenString } from "../domain/types"; +import type { TokenDto } from "../services/api-client/api-schemas"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +export const getNetworkLogo = (network: string) => + `https://assets.stakek.it/networks/${network}.svg`; + +export const getTokenLogo = (tokenSymbol: string) => + `https://assets.stakek.it/tokens/${tokenSymbol.toLowerCase()}.svg`; + +export const getTokenString = (token: TokenDto): TokenString => + `${token.network}-${token.address}`; + +export const truncateAddress = (address: string, length: number = 6) => + `${address.slice(0, length)}...${address.slice(-length)}`; + +export const formatSnakeCase = (network: string) => { + let str = network[0]?.toUpperCase() ?? ""; + for (let i = 1; i < network.length; i++) { + str += + network[i] === "_" ? ` ${network[++i]?.toUpperCase() ?? ""}` : network[i]; + } + return str; +}; + +export const formatDate = (timestamp: number): string => { + const date = new Date(timestamp); + return date.toLocaleDateString("en-US", { + day: "numeric", + month: "short", + hour: "numeric", + minute: "2-digit", + hour12: true, + }); +}; diff --git a/src/services/api-client/api-schemas.ts b/packages/common/src/services/api-client/api-schemas.ts similarity index 81% rename from src/services/api-client/api-schemas.ts rename to packages/common/src/services/api-client/api-schemas.ts index d87b22e..ae27cc2 100644 --- a/src/services/api-client/api-schemas.ts +++ b/packages/common/src/services/api-client/api-schemas.ts @@ -42,7 +42,7 @@ export class ProviderDto extends S.Class("ProviderDto")({ /** * Supported action types */ -"supportedActions": S.Array(S.Literal("open", "close", "updateLeverage", "stopLoss", "takeProfit", "cancelOrder", "fund", "withdraw")), +"supportedActions": S.Array(S.Literal("open", "close", "updateLeverage", "stopLoss", "takeProfit", "cancelOrder", "fund", "withdraw", "approveAgent")), /** * Argument schemas for each supported action (JSON Schema format) */ @@ -86,18 +86,30 @@ export class MarketsControllerGetMarketsParams extends S.Struct({ /** * Network identifier */ -export class Networks extends S.Literal("ethereum", "ethereum-goerli", "ethereum-holesky", "ethereum-sepolia", "ethereum-hoodi", "arbitrum", "base", "base-sepolia", "gnosis", "optimism", "polygon", "polygon-amoy", "starknet", "zksync", "linea", "unichain", "monad-testnet", "monad", "avalanche-c", "avalanche-c-atomic", "avalanche-p", "binance", "celo", "fantom", "harmony", "moonriver", "okc", "viction", "core", "sonic", "plasma", "katana", "hyperevm", "agoric", "akash", "axelar", "band-protocol", "bitsong", "canto", "chihuahua", "comdex", "coreum", "cosmos", "crescent", "cronos", "cudos", "desmos", "dydx", "evmos", "fetch-ai", "gravity-bridge", "injective", "irisnet", "juno", "kava", "ki-network", "mars-protocol", "nym", "okex-chain", "onomy", "osmosis", "persistence", "quicksilver", "regen", "secret", "sentinel", "sommelier", "stafi", "stargaze", "stride", "teritori", "tgrade", "umee", "sei", "mantra", "celestia", "saga", "zetachain", "dymension", "humansai", "neutron", "polkadot", "kusama", "westend", "bittensor", "binancebeacon", "cardano", "near", "solana", "solana-devnet", "stellar", "stellar-testnet", "sui", "tezos", "tron", "ton", "ton-testnet", "hyperliquid") {} +export class Networks extends S.Literal("ethereum", "ethereum-goerli", "ethereum-holesky", "ethereum-sepolia", "ethereum-hoodi", "arbitrum", "base", "base-sepolia", "gnosis", "optimism", "polygon", "polygon-amoy", "starknet", "zksync", "linea", "unichain", "monad-testnet", "monad", "avalanche-c", "avalanche-c-atomic", "avalanche-p", "binance", "celo", "fantom", "harmony", "moonriver", "okc", "viction", "core", "sonic", "plasma", "katana", "hyperevm", "agoric", "akash", "axelar", "band-protocol", "bitsong", "canto", "chihuahua", "comdex", "coreum", "cosmos", "crescent", "cronos", "cudos", "desmos", "dydx", "evmos", "fetch-ai", "gravity-bridge", "injective", "irisnet", "juno", "kava", "ki-network", "mars-protocol", "nym", "okex-chain", "onomy", "osmosis", "persistence", "quicksilver", "regen", "secret", "sentinel", "sommelier", "stafi", "stargaze", "stride", "teritori", "tgrade", "umee", "sei", "mantra", "celestia", "saga", "zetachain", "dymension", "humansai", "neutron", "polkadot", "kusama", "westend", "bittensor", "aptos", "binancebeacon", "cardano", "near", "solana", "solana-devnet", "stellar", "stellar-testnet", "sui", "tezos", "tron", "ton", "ton-testnet", "hyperliquid") {} export class TokenDto extends S.Class("TokenDto")({ - "name": S.String, + /** +* Token symbol +*/ +"symbol": S.String, + /** +* Token name +*/ +"name": S.optionalWith(S.String, { nullable: true }), "network": Networks, - "symbol": S.String, - "decimals": S.Number, - "address": S.optionalWith(S.String, { nullable: true }), - "coinGeckoId": S.optionalWith(S.String, { nullable: true }), - "logoURI": S.optionalWith(S.String, { nullable: true }), - "isPoints": S.optionalWith(S.Boolean, { nullable: true }), - "feeConfigurationId": S.optionalWith(S.String, { nullable: true }) + /** +* Token decimals +*/ +"decimals": S.optionalWith(S.Number, { nullable: true }), + /** +* Token contract address (optional for native tokens) +*/ +"address": S.optionalWith(S.String, { nullable: true }), + /** +* Token logo URI +*/ +"logoURI": S.optionalWith(S.String, { nullable: true }) }) {} export class MarketMetadataDto extends S.Class("MarketMetadataDto")({ @@ -235,7 +247,7 @@ export class PositionDtoMarginMode extends S.Literal("cross", "isolated") {} /** * Action type */ -export class PendingActionDtoType extends S.Literal("open", "close", "updateLeverage", "stopLoss", "takeProfit", "cancelOrder", "fund", "withdraw") {} +export class PendingActionDtoType extends S.Literal("open", "close", "updateLeverage", "stopLoss", "takeProfit", "cancelOrder", "fund", "withdraw", "approveAgent") {} /** * Position side - long (buy) or short (sell) @@ -303,7 +315,19 @@ export class ArgumentsDto extends S.Class("ArgumentsDto")({ /** * Source token for cross-chain funding (bridge/swap from another chain) */ -"fromToken": S.optionalWith(TokenIdentifierDto, { nullable: true }) +"fromToken": S.optionalWith(TokenIdentifierDto, { nullable: true }), + /** +* Agent wallet address to approve (for approveAgent action) +*/ +"agentAddress": S.optionalWith(S.String, { nullable: true }), + /** +* Name for the agent wallet (for approveAgent action). If not provided, defaults to "key". +*/ +"agentName": S.optionalWith(S.String, { nullable: true }), + /** +* Unix timestamp (seconds) when agent approval expires. If not provided, never expires. +*/ +"validUntil": S.optionalWith(S.Number, { nullable: true }) }) {} export class PendingActionDto extends S.Class("PendingActionDto")({ @@ -490,7 +514,7 @@ export class PortfolioControllerGetBalances429 extends S.Struct({ /** * Action to execute */ -export class ActionRequestDtoAction extends S.Literal("open", "close", "updateLeverage", "stopLoss", "takeProfit", "cancelOrder", "fund", "withdraw") {} +export class ActionRequestDtoAction extends S.Literal("open", "close", "updateLeverage", "stopLoss", "takeProfit", "cancelOrder", "fund", "withdraw", "approveAgent") {} export class ActionRequestDto extends S.Class("ActionRequestDto")({ /** @@ -514,7 +538,7 @@ export class ActionRequestDto extends S.Class("ActionRequestDt /** * Action type executed */ -export class ActionDtoAction extends S.Literal("open", "close", "updateLeverage", "stopLoss", "takeProfit", "cancelOrder", "fund", "withdraw") {} +export class ActionDtoAction extends S.Literal("open", "close", "updateLeverage", "stopLoss", "takeProfit", "cancelOrder", "fund", "withdraw", "approveAgent") {} /** * Current action status @@ -524,7 +548,7 @@ export class ActionDtoStatus extends S.Literal("CANCELED", "CREATED", "WAITING_F /** * Transaction type */ -export class TransactionDtoType extends S.Literal("APPROVAL", "OPEN_POSITION", "CLOSE_POSITION", "UPDATE_LEVERAGE", "STOP_LOSS", "TAKE_PROFIT", "CANCEL_ORDER", "FUND", "WITHDRAW") {} +export class TransactionDtoType extends S.Literal("APPROVAL", "OPEN_POSITION", "CLOSE_POSITION", "UPDATE_LEVERAGE", "STOP_LOSS", "TAKE_PROFIT", "CANCEL_ORDER", "FUND", "WITHDRAW", "APPROVE_BUILDER_FEE", "ENABLE_DEX_ABSTRACTION", "APPROVE_AGENT") {} /** * Current transaction status @@ -686,79 +710,6 @@ export class HealthStatusDto extends S.Class("HealthStatusDto") "timestamp": S.String }) {} -export class TokenControllerGetTokenPricesParams extends S.Struct({ - "X-API-KEY": S.optionalWith(S.String, { nullable: true }) -}) {} - -export class PriceRequestDto extends S.Class("PriceRequestDto")({ - "currency": S.String, - "tokenList": S.Array(TokenDto) -}) {} - -export class PriceResponseDto extends S.Class("PriceResponseDto")({ - -}) {} - -export class StakeKitErrorDto extends S.Class("StakeKitErrorDto")({ - "message": S.String, - "code": S.Number, - "type": S.optionalWith(S.String, { nullable: true }), - "details": S.optionalWith(S.Record({ key: S.String, value: S.Unknown }), { nullable: true }), - "path": S.optionalWith(S.String, { nullable: true }) -}) {} - -export class TokenControllerGetTokenBalancesParams extends S.Struct({ - "X-API-KEY": S.optionalWith(S.String, { nullable: true }) -}) {} - -export class CosmosAdditionalAddressesDto extends S.Class("CosmosAdditionalAddressesDto")({ - /** -* Cosmos SDK public key encoded in base64 for secp256k1 -*/ -"cosmosPubKey": S.String.pipe(S.minLength(44), S.maxLength(44)) -}) {} - -export class BinanceAdditionalAddressesDto extends S.Class("BinanceAdditionalAddressesDto")({ - "binanceBeaconAddress": S.String -}) {} - -export class SolanaAdditionalAddressesDto extends S.Class("SolanaAdditionalAddressesDto")({ - "stakeAccounts": S.Array(S.String), - "lidoStakeAccounts": S.Array(S.String) -}) {} - -export class TezosAdditionalAddressesDto extends S.Class("TezosAdditionalAddressesDto")({ - "tezosPubKey": S.String -}) {} - -export class AvalancheCAdditionalAddressesDto extends S.Class("AvalancheCAdditionalAddressesDto")({ - "cAddressBech": S.String, - "pAddressBech": S.String -}) {} - -export class AddressWithTokenDto extends S.Class("AddressWithTokenDto")({ - "address": S.String, - "additionalAddresses": S.optionalWith(S.Union(CosmosAdditionalAddressesDto, -BinanceAdditionalAddressesDto, -SolanaAdditionalAddressesDto, -TezosAdditionalAddressesDto, -AvalancheCAdditionalAddressesDto), { nullable: true }), - "network": Networks, - "tokenAddress": S.optionalWith(S.String, { nullable: true }) -}) {} - -export class BalancesRequestDto extends S.Class("BalancesRequestDto")({ - "addresses": S.Array(AddressWithTokenDto) -}) {} - -export class BalanceResponseDto extends S.Class("BalanceResponseDto")({ - "token": TokenDto, - "amount": S.String, - "availableYields": S.Array(S.String) -}) {} - -export class TokenControllerGetTokenBalances200 extends S.Array(BalanceResponseDto) {} - export const make = ( httpClient: HttpClient.HttpClient, options: { @@ -891,40 +842,6 @@ export const make = ( "2xx": decodeSuccess(HealthStatusDto), orElse: unexpectedStatus })) - ), - "TokenControllerGetTokenPrices": (options) => HttpClientRequest.post(`/v1/tokens/prices`).pipe( - HttpClientRequest.setHeaders({ "X-API-KEY": options.params?.["X-API-KEY"] ?? undefined }), - HttpClientRequest.bodyUnsafeJson(options.payload), - withResponse(HttpClientResponse.matchStatus({ - "2xx": decodeSuccess(PriceResponseDto), - "400": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "401": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "404": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "408": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "412": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "429": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "500": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "501": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "503": decodeError("StakeKitErrorDto", StakeKitErrorDto), - orElse: unexpectedStatus - })) - ), - "TokenControllerGetTokenBalances": (options) => HttpClientRequest.post(`/v1/tokens/balances`).pipe( - HttpClientRequest.setHeaders({ "X-API-KEY": options.params?.["X-API-KEY"] ?? undefined }), - HttpClientRequest.bodyUnsafeJson(options.payload), - withResponse(HttpClientResponse.matchStatus({ - "2xx": decodeSuccess(TokenControllerGetTokenBalances200), - "400": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "401": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "404": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "408": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "412": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "429": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "500": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "501": decodeError("StakeKitErrorDto", StakeKitErrorDto), - "503": decodeError("StakeKitErrorDto", StakeKitErrorDto), - orElse: unexpectedStatus - })) ) } } @@ -971,14 +888,6 @@ readonly "TransactionsControllerSubmitTransaction": (transactionId: string, opti * Get the health status of the perps API with current timestamp */ readonly "HealthControllerHealth": () => Effect.Effect - /** -* Returns the token prices for a specific list of tokens -*/ -readonly "TokenControllerGetTokenPrices": (options: { readonly params?: typeof TokenControllerGetTokenPricesParams.Encoded | undefined; readonly payload: typeof PriceRequestDto.Encoded }) => Effect.Effect | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type>> - /** -* Returns the balances for specific addresses and token addresses -*/ -readonly "TokenControllerGetTokenBalances": (options: { readonly params?: typeof TokenControllerGetTokenBalancesParams.Encoded | undefined; readonly payload: typeof BalancesRequestDto.Encoded }) => Effect.Effect | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type> | SKClientError<"StakeKitErrorDto", typeof StakeKitErrorDto.Type>> } export interface SKClientError { diff --git a/src/services/api-client/client-factory.ts b/packages/common/src/services/api-client/client-factory.ts similarity index 83% rename from src/services/api-client/client-factory.ts rename to packages/common/src/services/api-client/client-factory.ts index 0c92fed..3ebcc5a 100644 --- a/src/services/api-client/client-factory.ts +++ b/packages/common/src/services/api-client/client-factory.ts @@ -40,7 +40,7 @@ readonly "metadata": ProviderMetadataDto; /** * Supported action types */ -readonly "supportedActions": ReadonlyArray<"open" | "close" | "updateLeverage" | "stopLoss" | "takeProfit" | "cancelOrder" | "fund" | "withdraw">; +readonly "supportedActions": ReadonlyArray<"open" | "close" | "updateLeverage" | "stopLoss" | "takeProfit" | "cancelOrder" | "fund" | "withdraw" | "approveAgent">; /** * Argument schemas for each supported action (JSON Schema format) */ @@ -84,18 +84,30 @@ export interface MarketsControllerGetMarketsParams { /** * Network identifier */ -export type Networks = "ethereum" | "ethereum-goerli" | "ethereum-holesky" | "ethereum-sepolia" | "ethereum-hoodi" | "arbitrum" | "base" | "base-sepolia" | "gnosis" | "optimism" | "polygon" | "polygon-amoy" | "starknet" | "zksync" | "linea" | "unichain" | "monad-testnet" | "monad" | "avalanche-c" | "avalanche-c-atomic" | "avalanche-p" | "binance" | "celo" | "fantom" | "harmony" | "moonriver" | "okc" | "viction" | "core" | "sonic" | "plasma" | "katana" | "hyperevm" | "agoric" | "akash" | "axelar" | "band-protocol" | "bitsong" | "canto" | "chihuahua" | "comdex" | "coreum" | "cosmos" | "crescent" | "cronos" | "cudos" | "desmos" | "dydx" | "evmos" | "fetch-ai" | "gravity-bridge" | "injective" | "irisnet" | "juno" | "kava" | "ki-network" | "mars-protocol" | "nym" | "okex-chain" | "onomy" | "osmosis" | "persistence" | "quicksilver" | "regen" | "secret" | "sentinel" | "sommelier" | "stafi" | "stargaze" | "stride" | "teritori" | "tgrade" | "umee" | "sei" | "mantra" | "celestia" | "saga" | "zetachain" | "dymension" | "humansai" | "neutron" | "polkadot" | "kusama" | "westend" | "bittensor" | "binancebeacon" | "cardano" | "near" | "solana" | "solana-devnet" | "stellar" | "stellar-testnet" | "sui" | "tezos" | "tron" | "ton" | "ton-testnet" | "hyperliquid" +export type Networks = "ethereum" | "ethereum-goerli" | "ethereum-holesky" | "ethereum-sepolia" | "ethereum-hoodi" | "arbitrum" | "base" | "base-sepolia" | "gnosis" | "optimism" | "polygon" | "polygon-amoy" | "starknet" | "zksync" | "linea" | "unichain" | "monad-testnet" | "monad" | "avalanche-c" | "avalanche-c-atomic" | "avalanche-p" | "binance" | "celo" | "fantom" | "harmony" | "moonriver" | "okc" | "viction" | "core" | "sonic" | "plasma" | "katana" | "hyperevm" | "agoric" | "akash" | "axelar" | "band-protocol" | "bitsong" | "canto" | "chihuahua" | "comdex" | "coreum" | "cosmos" | "crescent" | "cronos" | "cudos" | "desmos" | "dydx" | "evmos" | "fetch-ai" | "gravity-bridge" | "injective" | "irisnet" | "juno" | "kava" | "ki-network" | "mars-protocol" | "nym" | "okex-chain" | "onomy" | "osmosis" | "persistence" | "quicksilver" | "regen" | "secret" | "sentinel" | "sommelier" | "stafi" | "stargaze" | "stride" | "teritori" | "tgrade" | "umee" | "sei" | "mantra" | "celestia" | "saga" | "zetachain" | "dymension" | "humansai" | "neutron" | "polkadot" | "kusama" | "westend" | "bittensor" | "aptos" | "binancebeacon" | "cardano" | "near" | "solana" | "solana-devnet" | "stellar" | "stellar-testnet" | "sui" | "tezos" | "tron" | "ton" | "ton-testnet" | "hyperliquid" export interface TokenDto { - readonly "name": string; + /** +* Token symbol +*/ +readonly "symbol": string; + /** +* Token name +*/ +readonly "name"?: string | undefined; readonly "network": Networks; - readonly "symbol": string; - readonly "decimals": number; - readonly "address"?: string | undefined; - readonly "coinGeckoId"?: string | undefined; - readonly "logoURI"?: string | undefined; - readonly "isPoints"?: boolean | undefined; - readonly "feeConfigurationId"?: string | undefined + /** +* Token decimals +*/ +readonly "decimals"?: number | undefined; + /** +* Token contract address (optional for native tokens) +*/ +readonly "address"?: string | undefined; + /** +* Token logo URI +*/ +readonly "logoURI"?: string | undefined } export interface MarketMetadataDto { @@ -233,7 +245,7 @@ export type PositionDtoMarginMode = "cross" | "isolated" /** * Action type */ -export type PendingActionDtoType = "open" | "close" | "updateLeverage" | "stopLoss" | "takeProfit" | "cancelOrder" | "fund" | "withdraw" +export type PendingActionDtoType = "open" | "close" | "updateLeverage" | "stopLoss" | "takeProfit" | "cancelOrder" | "fund" | "withdraw" | "approveAgent" /** * Position side - long (buy) or short (sell) @@ -301,7 +313,19 @@ readonly "assetIndex"?: number | undefined; /** * Source token for cross-chain funding (bridge/swap from another chain) */ -readonly "fromToken"?: TokenIdentifierDto | undefined +readonly "fromToken"?: TokenIdentifierDto | undefined; + /** +* Agent wallet address to approve (for approveAgent action) +*/ +readonly "agentAddress"?: string | undefined; + /** +* Name for the agent wallet (for approveAgent action). If not provided, defaults to "key". +*/ +readonly "agentName"?: string | undefined; + /** +* Unix timestamp (seconds) when agent approval expires. If not provided, never expires. +*/ +readonly "validUntil"?: number | undefined } export interface PendingActionDto { @@ -488,7 +512,7 @@ export interface PortfolioControllerGetBalances429 { /** * Action to execute */ -export type ActionRequestDtoAction = "open" | "close" | "updateLeverage" | "stopLoss" | "takeProfit" | "cancelOrder" | "fund" | "withdraw" +export type ActionRequestDtoAction = "open" | "close" | "updateLeverage" | "stopLoss" | "takeProfit" | "cancelOrder" | "fund" | "withdraw" | "approveAgent" export interface ActionRequestDto { /** @@ -512,7 +536,7 @@ readonly "args": ArgumentsDto /** * Action type executed */ -export type ActionDtoAction = "open" | "close" | "updateLeverage" | "stopLoss" | "takeProfit" | "cancelOrder" | "fund" | "withdraw" +export type ActionDtoAction = "open" | "close" | "updateLeverage" | "stopLoss" | "takeProfit" | "cancelOrder" | "fund" | "withdraw" | "approveAgent" /** * Current action status @@ -522,7 +546,7 @@ export type ActionDtoStatus = "CANCELED" | "CREATED" | "WAITING_FOR_NEXT" | "PRO /** * Transaction type */ -export type TransactionDtoType = "APPROVAL" | "OPEN_POSITION" | "CLOSE_POSITION" | "UPDATE_LEVERAGE" | "STOP_LOSS" | "TAKE_PROFIT" | "CANCEL_ORDER" | "FUND" | "WITHDRAW" +export type TransactionDtoType = "APPROVAL" | "OPEN_POSITION" | "CLOSE_POSITION" | "UPDATE_LEVERAGE" | "STOP_LOSS" | "TAKE_PROFIT" | "CANCEL_ORDER" | "FUND" | "WITHDRAW" | "APPROVE_BUILDER_FEE" | "ENABLE_DEX_ABSTRACTION" | "APPROVE_AGENT" /** * Current transaction status @@ -684,75 +708,6 @@ export interface HealthStatusDto { readonly "timestamp": string } -export interface TokenControllerGetTokenPricesParams { - readonly "X-API-KEY"?: string | undefined -} - -export interface PriceRequestDto { - readonly "currency": string; - readonly "tokenList": ReadonlyArray -} - -export interface PriceResponseDto { - -} - -export interface StakeKitErrorDto { - readonly "message": string; - readonly "code": number; - readonly "type"?: string | undefined; - readonly "details"?: Record | undefined; - readonly "path"?: string | undefined -} - -export interface TokenControllerGetTokenBalancesParams { - readonly "X-API-KEY"?: string | undefined -} - -export interface CosmosAdditionalAddressesDto { - /** -* Cosmos SDK public key encoded in base64 for secp256k1 -*/ -readonly "cosmosPubKey": string -} - -export interface BinanceAdditionalAddressesDto { - readonly "binanceBeaconAddress": string -} - -export interface SolanaAdditionalAddressesDto { - readonly "stakeAccounts": ReadonlyArray; - readonly "lidoStakeAccounts": ReadonlyArray -} - -export interface TezosAdditionalAddressesDto { - readonly "tezosPubKey": string -} - -export interface AvalancheCAdditionalAddressesDto { - readonly "cAddressBech": string; - readonly "pAddressBech": string -} - -export interface AddressWithTokenDto { - readonly "address": string; - readonly "additionalAddresses"?: CosmosAdditionalAddressesDto | BinanceAdditionalAddressesDto | SolanaAdditionalAddressesDto | TezosAdditionalAddressesDto | AvalancheCAdditionalAddressesDto | undefined; - readonly "network": Networks; - readonly "tokenAddress"?: string | undefined -} - -export interface BalancesRequestDto { - readonly "addresses": ReadonlyArray -} - -export interface BalanceResponseDto { - readonly "token": TokenDto; - readonly "amount": string; - readonly "availableYields": ReadonlyArray -} - -export type TokenControllerGetTokenBalances200 = ReadonlyArray - export const make = ( httpClient: HttpClient.HttpClient, options: { @@ -856,16 +811,6 @@ export const make = ( ), "HealthControllerHealth": () => HttpClientRequest.get(`/health`).pipe( onRequest(["2xx"]) - ), - "TokenControllerGetTokenPrices": (options) => HttpClientRequest.post(`/v1/tokens/prices`).pipe( - HttpClientRequest.setHeaders({ "X-API-KEY": options.params?.["X-API-KEY"] ?? undefined }), - HttpClientRequest.bodyUnsafeJson(options.payload), - onRequest(["2xx"], {"400":"StakeKitErrorDto","401":"StakeKitErrorDto","404":"StakeKitErrorDto","408":"StakeKitErrorDto","412":"StakeKitErrorDto","429":"StakeKitErrorDto","500":"StakeKitErrorDto","501":"StakeKitErrorDto","503":"StakeKitErrorDto"}) - ), - "TokenControllerGetTokenBalances": (options) => HttpClientRequest.post(`/v1/tokens/balances`).pipe( - HttpClientRequest.setHeaders({ "X-API-KEY": options.params?.["X-API-KEY"] ?? undefined }), - HttpClientRequest.bodyUnsafeJson(options.payload), - onRequest(["2xx"], {"400":"StakeKitErrorDto","401":"StakeKitErrorDto","404":"StakeKitErrorDto","408":"StakeKitErrorDto","412":"StakeKitErrorDto","429":"StakeKitErrorDto","500":"StakeKitErrorDto","501":"StakeKitErrorDto","503":"StakeKitErrorDto"}) ) } } @@ -912,14 +857,6 @@ readonly "TransactionsControllerSubmitTransaction": (transactionId: string, opti * Get the health status of the perps API with current timestamp */ readonly "HealthControllerHealth": () => Effect.Effect - /** -* Returns the token prices for a specific list of tokens -*/ -readonly "TokenControllerGetTokenPrices": (options: { readonly params?: TokenControllerGetTokenPricesParams | undefined; readonly payload: PriceRequestDto }) => Effect.Effect | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto>> - /** -* Returns the balances for specific addresses and token addresses -*/ -readonly "TokenControllerGetTokenBalances": (options: { readonly params?: TokenControllerGetTokenBalancesParams | undefined; readonly payload: BalancesRequestDto }) => Effect.Effect | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto> | SKClientError<"StakeKitErrorDto", StakeKitErrorDto>> } export interface SKClientError { diff --git a/src/services/api-client/index.ts b/packages/common/src/services/api-client/index.ts similarity index 94% rename from src/services/api-client/index.ts rename to packages/common/src/services/api-client/index.ts index 3f0b9d3..6accf89 100644 --- a/src/services/api-client/index.ts +++ b/packages/common/src/services/api-client/index.ts @@ -1,8 +1,8 @@ import { HttpClient, HttpClientRequest } from "@effect/platform"; import { Effect, Schema } from "effect"; import { toast } from "sonner"; -import { ConfigService } from "@/services/config"; -import { HttpClientService } from "@/services/http-client"; +import { ConfigService } from "../config"; +import { HttpClientService } from "../http-client"; import * as ApiClientFactory from "./client-factory"; export class ApiClientService extends Effect.Service()( diff --git a/src/services/config.ts b/packages/common/src/services/config.ts similarity index 97% rename from src/services/config.ts rename to packages/common/src/services/config.ts index 651749d..7fd21da 100644 --- a/src/services/config.ts +++ b/packages/common/src/services/config.ts @@ -31,7 +31,6 @@ export class ConfigService extends Effect.Service()( }; }).pipe( Effect.withConfigProvider(ConfigProvider.fromJson(import.meta.env)), - Effect.orDie, ), }, ) {} diff --git a/src/services/constants.ts b/packages/common/src/services/constants.ts similarity index 100% rename from src/services/constants.ts rename to packages/common/src/services/constants.ts diff --git a/src/services/http-client/index.tsx b/packages/common/src/services/http-client/index.ts similarity index 100% rename from src/services/http-client/index.tsx rename to packages/common/src/services/http-client/index.ts diff --git a/packages/common/src/services/index.ts b/packages/common/src/services/index.ts new file mode 100644 index 0000000..5b34f1b --- /dev/null +++ b/packages/common/src/services/index.ts @@ -0,0 +1,12 @@ +export * from "./api-client"; +export * as ApiSchemas from "./api-client/api-schemas"; +// export type * from "./api-client/client-factory"; +export type * as ApiTypes from "./api-client/client-factory"; +export * from "./config"; +export * from "./constants"; +export * from "./http-client"; +export * from "./runtime"; +export * from "./wallet/browser-signer"; +export * from "./wallet/ledger-signer"; +export * from "./wallet/signer"; +export * from "./wallet/wallet-service"; diff --git a/src/services/runtime.ts b/packages/common/src/services/runtime.ts similarity index 68% rename from src/services/runtime.ts rename to packages/common/src/services/runtime.ts index fe41631..b90a64e 100644 --- a/src/services/runtime.ts +++ b/packages/common/src/services/runtime.ts @@ -1,12 +1,12 @@ import { Atom, Registry } from "@effect-atom/atom-react"; import { Cause, Effect, Layer, Logger } from "effect"; -import { ApiClientService } from "@/services/api-client"; -import { ConfigService } from "@/services/config"; -import { HttpClientService } from "@/services/http-client"; -import { BrowserSignerLayer } from "@/services/wallet/browser-signer"; -import { LedgerSignerLayer } from "@/services/wallet/ledger-signer"; -import { isLedgerDappBrowserProvider } from "@/services/wallet/ledger-signer/utils"; -import { WalletService } from "@/services/wallet/wallet-service"; +import { ApiClientService } from "./api-client"; +import { ConfigService } from "./config"; +import { HttpClientService } from "./http-client"; +import { BrowserSignerLayer } from "./wallet/browser-signer"; +import { LedgerSignerLayer } from "./wallet/ledger-signer"; +import { isLedgerDappBrowserProvider } from "./wallet/ledger-signer/utils"; +import { WalletService } from "./wallet/wallet-service"; const Signer = isLedgerDappBrowserProvider ? LedgerSignerLayer.pipe(Layer.orDie) diff --git a/src/services/wallet/browser-signer.ts b/packages/common/src/services/wallet/browser-signer.ts similarity index 96% rename from src/services/wallet/browser-signer.ts rename to packages/common/src/services/wallet/browser-signer.ts index 905c56b..9bc0dd6 100644 --- a/src/services/wallet/browser-signer.ts +++ b/packages/common/src/services/wallet/browser-signer.ts @@ -21,7 +21,7 @@ import { switchConnection, switchChain as wagmiSwitchChain, } from "wagmi/actions"; -import type { SupportedSKChains } from "@/domain/chains"; +import type { SupportedSKChains } from "../../domain/chains"; import { type AccountsState, type BrowserWalletAccount, @@ -29,14 +29,14 @@ import { SignTransactionError, SwitchAccountError, SwitchChainError, -} from "@/domain/signer"; +} from "../../domain/signer"; import { EIP712Tx, type Transaction, TransactionHash, -} from "@/domain/transactions"; -import { ConfigService } from "@/services/config"; -import { SignerService } from "@/services/wallet/signer"; +} from "../../domain/transactions"; +import { ConfigService } from "../config"; +import { SignerService } from "./signer"; const hyperLiquidL1 = defineChain({ id: 1337, diff --git a/src/services/wallet/ledger-signer/index.ts b/packages/common/src/services/wallet/ledger-signer/index.ts similarity index 94% rename from src/services/wallet/ledger-signer/index.ts rename to packages/common/src/services/wallet/ledger-signer/index.ts index 30c96d8..f204ee0 100644 --- a/src/services/wallet/ledger-signer/index.ts +++ b/packages/common/src/services/wallet/ledger-signer/index.ts @@ -10,28 +10,28 @@ import { Schema, SubscriptionRef, } from "effect"; -import { evmChainsMap } from "@/domain/chains/evm"; -import type { SupportedLedgerLiveFamilies } from "@/domain/chains/ledger"; +import { evmChainsMap } from "../../../domain/chains/evm"; +import type { SupportedLedgerLiveFamilies } from "../../../domain/chains/ledger"; import { type AccountsState, type LedgerWalletAccount, makeLedgerWalletAccount, SignTransactionError, SwitchAccountError, -} from "@/domain/signer"; +} from "../../../domain/signer"; import { EIP712Tx, EvmTx, type Transaction, TransactionHash, -} from "@/domain/transactions"; +} from "../../../domain/transactions"; +import { SignerService } from "../signer"; import { getFilteredSupportedLedgerFamiliesWithCurrency, getLedgerAccounts, getLedgerCurrencies, NoAccountsFoundError, -} from "@/services/wallet/ledger-signer/utils"; -import { SignerService } from "@/services/wallet/signer"; +} from "./utils"; export const LedgerSignerLayer = Effect.gen(function* () { const transport = new WindowMessageTransport(); diff --git a/src/services/wallet/ledger-signer/utils.ts b/packages/common/src/services/wallet/ledger-signer/utils.ts similarity index 93% rename from src/services/wallet/ledger-signer/utils.ts rename to packages/common/src/services/wallet/ledger-signer/utils.ts index 50f0f01..fa6bfcd 100644 --- a/src/services/wallet/ledger-signer/utils.ts +++ b/packages/common/src/services/wallet/ledger-signer/utils.ts @@ -6,12 +6,12 @@ import type { WalletAPIClient, } from "@ledgerhq/wallet-api-client"; import { Data, Effect, Option, Record } from "effect"; -import type { SupportedSKChains } from "@/domain/chains"; -import type { EvmChainsMap } from "@/domain/chains/evm"; +import type { SupportedSKChains } from "../../../domain/chains"; +import type { EvmChainsMap } from "../../../domain/chains/evm"; import { type SupportedLedgerLiveFamilies, supportedLedgerFamiliesWithCurrency, -} from "@/domain/chains/ledger"; +} from "../../../domain/chains/ledger"; export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ accounts, @@ -67,7 +67,11 @@ export const getFilteredSupportedLedgerFamiliesWithCurrency = ({ type SubItemKey = keyof typeof subItem; const subItemMap = Object.keys(subItem).reduce((acc, subKey) => { - acc.set(subKey as SubItemKey, subItem[subKey as keyof typeof subItem]); + const val = Record.get(subItem, subKey).pipe(Option.getOrNull); + + if (!val) return acc; + + acc.set(subKey as SubItemKey, val); return acc; }, new Map()); diff --git a/src/services/wallet/signer.ts b/packages/common/src/services/wallet/signer.ts similarity index 75% rename from src/services/wallet/signer.ts rename to packages/common/src/services/wallet/signer.ts index b616e39..e178c37 100644 --- a/src/services/wallet/signer.ts +++ b/packages/common/src/services/wallet/signer.ts @@ -1,5 +1,5 @@ import { Context } from "effect"; -import type { Signer } from "@/domain/signer"; +import type { Signer } from "../../domain/signer"; export class SignerService extends Context.Tag( "perps/services/wallet/signer/SignerService", diff --git a/src/services/wallet/wallet-service.ts b/packages/common/src/services/wallet/wallet-service.ts similarity index 97% rename from src/services/wallet/wallet-service.ts rename to packages/common/src/services/wallet/wallet-service.ts index 4d46f2a..d377193 100644 --- a/src/services/wallet/wallet-service.ts +++ b/packages/common/src/services/wallet/wallet-service.ts @@ -9,17 +9,17 @@ import { Stream, SubscriptionRef, } from "effect"; -import type { BrowserSigner, LedgerSigner } from "@/domain/signer"; -import { Transaction } from "@/domain/transactions"; +import type { BrowserSigner, LedgerSigner } from "../../domain/signer"; +import { Transaction } from "../../domain/transactions"; import { type SignTransactionsState, TransactionFailedError, TransactionNotConfirmedError, type Wallet, -} from "@/domain/wallet"; -import { ApiClientService } from "@/services/api-client"; -import type { ActionDto } from "@/services/api-client/api-schemas"; -import { SignerService } from "@/services/wallet/signer"; +} from "../../domain/wallet"; +import { ApiClientService } from "../api-client"; +import type { ActionDto } from "../api-client/api-schemas"; +import { SignerService } from "./signer"; export class WalletService extends Effect.Service()( "perps/services/wallet-service/WalletService", diff --git a/packages/common/src/styles/base.css b/packages/common/src/styles/base.css new file mode 100644 index 0000000..a76d29a --- /dev/null +++ b/packages/common/src/styles/base.css @@ -0,0 +1,24 @@ +html { + interpolate-size: allow-keywords; +} + +body { + @apply m-0; + font-family: var(--font-family); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: + source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/packages/common/src/styles/index.css b/packages/common/src/styles/index.css new file mode 100644 index 0000000..1d0eae3 --- /dev/null +++ b/packages/common/src/styles/index.css @@ -0,0 +1,6 @@ +@import "tailwindcss"; +@import "tw-animate-css"; +@import "./theme.css"; +@import "./base.css"; + +@custom-variant dark (&:is(.dark *)); diff --git a/packages/common/src/styles/theme.css b/packages/common/src/styles/theme.css new file mode 100644 index 0000000..2c8839d --- /dev/null +++ b/packages/common/src/styles/theme.css @@ -0,0 +1,97 @@ +:root { + --font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", + "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --primary: oklch(0.21 0.006 285.885); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.871 0.006 286.286); + --radius: 0.625rem; + + --gray-1: #f6f7f999; + --gray-2: #999999; + --gray-3: #ffffff0d; + --gray-4: #ffffff40; + --gray-5: #ffffff2e; + + --surface-1: #131517; + --surface-2: #121314; + --surface-3: #090909; + + --accent-green: #34c759; + --accent-red: #ff383c; +} + +.dark { + --background: #000000b2; + --foreground: oklch(0.985 0 0); + --card: oklch(0.141 0.005 285.823); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.21 0.006 285.885); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --destructive: oklch(0.396 0.141 25.723); + --border: oklch(0.274 0.006 286.033); + --input: oklch(0.274 0.006 286.033); + --ring: oklch(0.442 0.017 285.786); + + --gray-1: #f6f7f999; + --gray-2: #999999; + --gray-3: #ffffff0d; + --gray-4: #ffffff40; + --gray-5: #ffffff2e; + + --surface-1: #131517; + --surface-2: #121314; + --surface-3: #090909; + + --accent-green: #34c759; + --accent-red: #ff383c; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + + --color-gray-1: var(--gray-1); + --color-gray-2: var(--gray-2); + --color-gray-3: var(--gray-3); + --color-gray-4: var(--gray-4); + --color-gray-5: var(--gray-5); + + --color-surface-1: var(--surface-1); + --color-surface-2: var(--surface-2); + --color-surface-3: var(--surface-3); + + --color-accent-green: var(--accent-green); + --color-accent-red: var(--accent-red); +} diff --git a/vite.config.ts b/packages/common/src/vite.config.ts similarity index 60% rename from vite.config.ts rename to packages/common/src/vite.config.ts index 586af8b..8c6e592 100644 --- a/vite.config.ts +++ b/packages/common/src/vite.config.ts @@ -1,10 +1,8 @@ -import { fileURLToPath, URL } from "node:url"; import tailwindcss from "@tailwindcss/vite"; -import { devtools } from "@tanstack/devtools-vite"; import { tanstackRouter } from "@tanstack/router-plugin/vite"; import viteReact from "@vitejs/plugin-react"; import { playwright } from "@vitest/browser-playwright"; -import { defineConfig } from "vite"; +import type { UserConfig } from "vite"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import type { InlineConfig } from "vitest/node"; @@ -14,27 +12,22 @@ declare module "vite" { } } -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - devtools(), - tanstackRouter({ - target: "react", - autoCodeSplitting: true, - }), - viteReact({ - babel: { - plugins: ["babel-plugin-react-compiler"], - }, - }), - tailwindcss(), - nodePolyfills({ include: ["buffer"] }), - ], - resolve: { - alias: { - "@": fileURLToPath(new URL("./src", import.meta.url)), +export const commonPlugins = { + tanstackRouter: tanstackRouter({ + target: "react", + autoCodeSplitting: true, + }), + viteReact: viteReact({ + babel: { + plugins: ["babel-plugin-react-compiler"], }, - }, + }), + tailwindcss: tailwindcss(), + nodePolyfills: nodePolyfills({ include: ["buffer"] }), +}; + +export const commonViteConfig: UserConfig = { + plugins: Object.values(commonPlugins), test: { browser: { screenshotFailures: false, @@ -53,4 +46,4 @@ export default defineConfig({ "vite-plugin-node-polyfills/shims/process", ], }, -}); +}; diff --git a/tests/components/leverage-dialog.test.tsx b/packages/common/tests/components/leverage-dialog.test.tsx similarity index 99% rename from tests/components/leverage-dialog.test.tsx rename to packages/common/tests/components/leverage-dialog.test.tsx index e6bc2f1..23f6b61 100644 --- a/tests/components/leverage-dialog.test.tsx +++ b/packages/common/tests/components/leverage-dialog.test.tsx @@ -4,7 +4,7 @@ import { render } from "vitest-browser-react"; import { LeverageDialog, LeverageDialogContent, -} from "@/components/modules/Order/Overview/leverage-dialog"; +} from "../../src/components/molecules/leverage-dialog"; import { TestWrapper } from "./wrapper"; const defaultProps = { diff --git a/tests/components/limit-price-dialog.test.tsx b/packages/common/tests/components/limit-price-dialog.test.tsx similarity index 99% rename from tests/components/limit-price-dialog.test.tsx rename to packages/common/tests/components/limit-price-dialog.test.tsx index ceb9334..bed8f75 100644 --- a/tests/components/limit-price-dialog.test.tsx +++ b/packages/common/tests/components/limit-price-dialog.test.tsx @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; import { userEvent } from "vitest/browser"; import { render } from "vitest-browser-react"; -import { LimitPriceDialog } from "@/components/modules/Order/Overview/limit-price-dialog"; +import { LimitPriceDialog } from "../../src/components/molecules/limit-price-dialog"; import { TestWrapper } from "./wrapper"; const defaultProps = { diff --git a/tests/components/order-type-dialog.test.tsx b/packages/common/tests/components/order-type-dialog.test.tsx similarity index 99% rename from tests/components/order-type-dialog.test.tsx rename to packages/common/tests/components/order-type-dialog.test.tsx index ba08cc0..5197288 100644 --- a/tests/components/order-type-dialog.test.tsx +++ b/packages/common/tests/components/order-type-dialog.test.tsx @@ -4,7 +4,7 @@ import { render } from "vitest-browser-react"; import { ORDER_TYPE_OPTIONS, OrderTypeDialog, -} from "@/components/modules/Order/Overview/order-type-dialog"; +} from "../../src/components/molecules/order-type-dialog"; import { TestWrapper } from "./wrapper"; const defaultProps = { diff --git a/tests/components/tp-sl-dialog.test.tsx b/packages/common/tests/components/tp-sl-dialog.test.tsx similarity index 99% rename from tests/components/tp-sl-dialog.test.tsx rename to packages/common/tests/components/tp-sl-dialog.test.tsx index a79e7b6..3452a20 100644 --- a/tests/components/tp-sl-dialog.test.tsx +++ b/packages/common/tests/components/tp-sl-dialog.test.tsx @@ -2,10 +2,10 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; import { userEvent } from "vitest/browser"; import { render } from "vitest-browser-react"; import { - getTPOrSLConfigurationFromPosition, TPOrSLDialog, type TPOrSLSettings, -} from "@/components/molecules/tp-sl-dialog"; +} from "../../src/components/molecules/tp-sl-dialog"; +import { getTPOrSLConfigurationFromPosition } from "../../src/lib/math"; import { TestWrapper } from "./wrapper"; const defaultSettings: TPOrSLSettings = { diff --git a/tests/components/wrapper.tsx b/packages/common/tests/components/wrapper.tsx similarity index 56% rename from tests/components/wrapper.tsx rename to packages/common/tests/components/wrapper.tsx index f2e12a0..bf4ea7c 100644 --- a/tests/components/wrapper.tsx +++ b/packages/common/tests/components/wrapper.tsx @@ -1,6 +1,6 @@ -import "../../src/styles.css"; -import { Toaster } from "@/components/ui/toaster"; -import { RootContainerProvider } from "@/context/root-container"; +import { RootContainerProvider } from "../../src/context/root-container"; +import "../../src/styles/index.css"; +import { Toaster } from "sonner"; export const TestWrapper = ({ children }: { children: React.ReactNode }) => { return ( diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json new file mode 100644 index 0000000..ac4d417 --- /dev/null +++ b/packages/common/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "noEmit": false, + "outDir": "./dist", + "rootDir": "." + }, + "include": ["src", "src/**/*.json", "scripts", "tests"] +} diff --git a/packages/common/vite.config.ts b/packages/common/vite.config.ts new file mode 100644 index 0000000..f961b21 --- /dev/null +++ b/packages/common/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vite"; +import { commonPlugins, commonViteConfig } from "./src/vite.config"; + +export default defineConfig({ + plugins: [ + commonPlugins.viteReact, + commonPlugins.tailwindcss, + commonPlugins.nodePolyfills, + ], + test: commonViteConfig.test, + optimizeDeps: commonViteConfig.optimizeDeps, +}); diff --git a/packages/dashboard/.env.example b/packages/dashboard/.env.example new file mode 100644 index 0000000..6dc0341 --- /dev/null +++ b/packages/dashboard/.env.example @@ -0,0 +1,12 @@ +# Perps API Configuration +VITE_PERPS_BASE_URL= +VITE_PERPS_API_KEY= + +# Reown (WalletConnect) - Get your project ID at https://cloud.reown.com +VITE_REOWN_PROJECT_ID= + +# Moralis API - Get your API key at https://moralis.io +VITE_MORALIS_API_KEY= + +# Optional: Perps OpenAPI docs URL (for code generation) +# VITE_PERPS_DOCS_URL= diff --git a/packages/dashboard/index.html b/packages/dashboard/index.html new file mode 100644 index 0000000..a8f4c88 --- /dev/null +++ b/packages/dashboard/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + Yield.xyz - Perps Dashboard + + +
+ + + diff --git a/packages/dashboard/package.json b/packages/dashboard/package.json new file mode 100644 index 0000000..35eb955 --- /dev/null +++ b/packages/dashboard/package.json @@ -0,0 +1,66 @@ +{ + "name": "@yieldxyz/perps-dashboard", + "type": "module", + "scripts": { + "dev": "vite --port 3001", + "build": "vite build && tsc -b", + "preview": "vite preview", + "lint": "biome check . && tsc -b", + "format": "biome format --write .", + "generate-routes": "tsr generate" + }, + "dependencies": { + "@yieldxyz/perps-common": "workspace:*", + "@base-ui/react": "catalog:", + "@effect-atom/atom-react": "catalog:", + "@effect/experimental": "^0.58.0", + "@effect/platform": "catalog:", + "@effect/platform-node": "catalog:", + "@ledgerhq/wallet-api-client": "catalog:", + "@lucas-barake/effect-form-react": "catalog:", + "@reown/appkit": "catalog:", + "@reown/appkit-adapter-wagmi": "catalog:", + "@stakekit/common": "catalog:", + "@tailwindcss/vite": "catalog:", + "@tanstack/react-devtools": "catalog:", + "@tanstack/react-query": "catalog:", + "@tanstack/react-router": "catalog:", + "@tanstack/react-router-devtools": "catalog:", + "@tanstack/react-virtual": "catalog:", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "effect": "catalog:", + "lucide-react": "catalog:", + "react": "catalog:", + "react-dom": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", + "tailwindcss": "catalog:", + "tw-animate-css": "catalog:", + "viem": "catalog:", + "wagmi": "catalog:" + }, + "devDependencies": { + "@tanstack/devtools-vite": "catalog:", + "@tanstack/router-cli": "catalog:", + "@testing-library/dom": "catalog:", + "@testing-library/react": "catalog:", + "@tim-smart/openapi-gen": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "@vite-pwa/assets-generator": "catalog:", + "@vitejs/plugin-react": "catalog:", + "@vitest/browser-playwright": "catalog:", + "babel-plugin-react-compiler": "catalog:", + "jsdom": "catalog:", + "openapi-filter": "catalog:", + "tsx": "catalog:", + "typescript": "catalog:", + "vite": "catalog:", + "vite-plugin-node-polyfills": "catalog:", + "vitest": "catalog:", + "vitest-browser-react": "catalog:", + "@tanstack/router-plugin": "catalog:" + } +} diff --git a/public/apple-touch-icon-180x180.png b/packages/dashboard/public/apple-touch-icon-180x180.png similarity index 100% rename from public/apple-touch-icon-180x180.png rename to packages/dashboard/public/apple-touch-icon-180x180.png diff --git a/public/favicon.ico b/packages/dashboard/public/favicon.ico similarity index 100% rename from public/favicon.ico rename to packages/dashboard/public/favicon.ico diff --git a/public/logo-192x192.png b/packages/dashboard/public/logo-192x192.png similarity index 100% rename from public/logo-192x192.png rename to packages/dashboard/public/logo-192x192.png diff --git a/public/logo-512x512.png b/packages/dashboard/public/logo-512x512.png similarity index 100% rename from public/logo-512x512.png rename to packages/dashboard/public/logo-512x512.png diff --git a/public/logo-64x64.png b/packages/dashboard/public/logo-64x64.png similarity index 100% rename from public/logo-64x64.png rename to packages/dashboard/public/logo-64x64.png diff --git a/public/maskable-icon-512x512.png b/packages/dashboard/public/maskable-icon-512x512.png similarity index 100% rename from public/maskable-icon-512x512.png rename to packages/dashboard/public/maskable-icon-512x512.png diff --git a/public/yield_xyz.png b/packages/dashboard/public/yield_xyz.png similarity index 100% rename from public/yield_xyz.png rename to packages/dashboard/public/yield_xyz.png diff --git a/packages/dashboard/src/app.tsx b/packages/dashboard/src/app.tsx new file mode 100644 index 0000000..b88372c --- /dev/null +++ b/packages/dashboard/src/app.tsx @@ -0,0 +1,44 @@ +import "./styles.css"; +import { createRouter, RouterProvider } from "@tanstack/react-router"; +import { Providers, useRootContainer } from "@yieldxyz/perps-common/context"; +import { Preload } from "./components/modules/root/Preload"; +import { SignTransactionsDialog } from "./components/modules/trade/order-form/sign-dialog"; +import { routeTree } from "./routeTree.gen"; + +const router = createRouter({ + routeTree, + context: {}, + defaultPreload: "intent", + scrollRestoration: true, + defaultStructuralSharing: true, + defaultPreloadStaleTime: 0, +}); + +declare module "@tanstack/react-router" { + interface Register { + router: typeof router; + } +} + +const App = () => { + const rootContainer = useRootContainer(); + + return ( +
+ + +
+ ); +}; + +export const Dashboard = () => { + return ( + + + + + ); +}; diff --git a/packages/dashboard/src/atoms/selected-market-atom.ts b/packages/dashboard/src/atoms/selected-market-atom.ts new file mode 100644 index 0000000..8271094 --- /dev/null +++ b/packages/dashboard/src/atoms/selected-market-atom.ts @@ -0,0 +1,22 @@ +import { Atom, type AtomRef, Result } from "@effect-atom/atom-react"; +import { MarketNotFoundError, marketsAtom } from "@yieldxyz/perps-common/atoms"; +import { type ApiTypes, runtimeAtom } from "@yieldxyz/perps-common/services"; +import { Array as _Array, Effect, Option, Record } from "effect"; + +const initMarketAtom = runtimeAtom.atom((ctx) => + ctx.resultOnce(marketsAtom).pipe( + Effect.andThen((markets) => + Record.findFirst(markets, (m) => m.value.baseAsset.symbol === "BTC").pipe( + Option.map((v) => v[1]), + Option.orElse(() => _Array.head(Record.values(markets))), + ), + ), + Effect.catchTag("NoSuchElementException", () => new MarketNotFoundError()), + ), +); + +export const selectedMarketAtom = Atom.writable( + (ctx) => ctx.get(initMarketAtom), + (ctx, value: AtomRef.AtomRef) => + ctx.setSelf(Result.success(value)), +); diff --git a/packages/dashboard/src/components/atoms/logo.tsx b/packages/dashboard/src/components/atoms/logo.tsx new file mode 100644 index 0000000..9610a08 --- /dev/null +++ b/packages/dashboard/src/components/atoms/logo.tsx @@ -0,0 +1,28 @@ +export const Logo = () => { + return ( + + Yield.xyz Logo + + + + + + + + + + + ); +}; diff --git a/packages/dashboard/src/components/modules/root/Preload.tsx b/packages/dashboard/src/components/modules/root/Preload.tsx new file mode 100644 index 0000000..654325d --- /dev/null +++ b/packages/dashboard/src/components/modules/root/Preload.tsx @@ -0,0 +1,45 @@ +import { Result, useAtomMount, useAtomValue } from "@effect-atom/atom-react"; +import { + marketsAtom, + moralisTokenBalancesAtom, + ordersAtom, + positionsAtom, + providersAtom, + providersBalancesAtom, + refreshMarketsAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { TRADING_VIEW_WIDGET_SCRIPT_URL } from "@yieldxyz/perps-common/services"; +import { preload } from "react-dom"; + +const PreloadWalletConnectedAtoms = ({ + wallet, +}: { + wallet: WalletConnected; +}) => { + useAtomMount(moralisTokenBalancesAtom(wallet.currentAccount.address)); + useAtomMount(providersBalancesAtom(wallet.currentAccount.address)); + useAtomMount(positionsAtom(wallet.currentAccount.address)); + useAtomMount(ordersAtom(wallet.currentAccount.address)); + + return null; +}; + +export const Preload = () => { + preload(TRADING_VIEW_WIDGET_SCRIPT_URL, { as: "script" }); + + const wallet = useAtomValue(walletAtom); + useAtomMount(providersAtom); + useAtomMount(marketsAtom); + useAtomMount(refreshMarketsAtom); + + if (Result.isSuccess(wallet) && isWalletConnected(wallet.value)) { + return ; + } + + return null; +}; diff --git a/packages/dashboard/src/components/modules/trade/chart.tsx b/packages/dashboard/src/components/modules/trade/chart.tsx new file mode 100644 index 0000000..ec9e352 --- /dev/null +++ b/packages/dashboard/src/components/modules/trade/chart.tsx @@ -0,0 +1,59 @@ +import { + type AtomRef, + Result, + useAtomRef, + useAtomValue, +} from "@effect-atom/atom-react"; +import { Chart as ChartView, Text } from "@yieldxyz/perps-common/components"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; +import { TriangleAlertIcon } from "lucide-react"; +import { selectedMarketAtom } from "../../../atoms/selected-market-atom"; + +const ChartLoading = () => ( +
+
+
+ + Loading chart... + +
+
+); + +const ChartError = ({ message }: { message: string }) => ( +
+
+
+ + {message} +
+
+
+); + +const ChartContent = ({ + marketRef, +}: { + marketRef: AtomRef.AtomRef; +}) => { + const market = useAtomRef(marketRef); + + return ; +}; + +export const Chart = () => { + const selectedMarketResult = useAtomValue(selectedMarketAtom); + + return ( +
+ {Result.matchWithWaiting(selectedMarketResult, { + onWaiting: () => , + onSuccess: ({ value: selectedMarket }) => ( + + ), + onError: () => , + onDefect: () => , + })} +
+ ); +}; diff --git a/packages/dashboard/src/components/modules/trade/index.tsx b/packages/dashboard/src/components/modules/trade/index.tsx new file mode 100644 index 0000000..5b45f83 --- /dev/null +++ b/packages/dashboard/src/components/modules/trade/index.tsx @@ -0,0 +1,20 @@ +import { PositionsTable } from "../../molecules/positions"; +import { Chart } from "./chart"; +import { MarketInfoBar } from "./market-info"; +import { OrderForm } from "./order-form"; + +export function TradePage() { + return ( +
+
+ + + + + +
+ + +
+ ); +} diff --git a/packages/dashboard/src/components/modules/trade/market-info/bar-skeleton.tsx b/packages/dashboard/src/components/modules/trade/market-info/bar-skeleton.tsx new file mode 100644 index 0000000..1fe229b --- /dev/null +++ b/packages/dashboard/src/components/modules/trade/market-info/bar-skeleton.tsx @@ -0,0 +1,60 @@ +import { Skeleton } from "@yieldxyz/perps-common/components"; +import { cn } from "@yieldxyz/perps-common/lib"; + +export function MarketInfoBarSkeleton({ className }: { className?: string }) { + return ( +
+ {/* Asset Selector Skeleton */} +
+ + + + +
+ + {/* Divider */} +
+ + {/* Price Skeleton */} +
+ + +
+ + {/* 24H Change Skeleton */} +
+ + +
+ + {/* 24H Volume Skeleton */} +
+ + +
+ + {/* Open Interest Skeleton */} +
+ + +
+ + {/* Maker Fee Skeleton */} +
+ + +
+ + {/* Taker Fee Skeleton */} +
+ + +
+
+ ); +} diff --git a/packages/dashboard/src/components/modules/trade/market-info/index.tsx b/packages/dashboard/src/components/modules/trade/market-info/index.tsx new file mode 100644 index 0000000..44801ea --- /dev/null +++ b/packages/dashboard/src/components/modules/trade/market-info/index.tsx @@ -0,0 +1,174 @@ +import { + type AtomRef, + Result, + useAtomRef, + useAtomSet, + useAtomValue, +} from "@effect-atom/atom-react"; +import { Popover, Text, TokenIcon } from "@yieldxyz/perps-common/components"; +import { + cn, + formatAmount, + formatCompactUsdAmount, + formatPercentage, + formatRate, + getTokenLogo, +} from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; +import { ChevronDown } from "lucide-react"; +import { useState } from "react"; +import { selectedMarketAtom } from "../../../../atoms/selected-market-atom"; +import { MarketInfoBarSkeleton } from "./bar-skeleton"; +import { MarketSelectorContent } from "./market-selector-popover"; + +interface MarketInfoBarProps { + className?: string; +} + +function MarketInfoBarContent({ + marketRef, + className, +}: { + marketRef: AtomRef.AtomRef; + className?: string; +}) { + const market = useAtomRef(marketRef); + const setSelectedMarket = useAtomSet(selectedMarketAtom); + const [isOpen, setIsOpen] = useState(false); + const isPositiveChange = market.priceChangePercent24h >= 0; + const logo = + market.baseAsset.logoURI ?? getTokenLogo(market.baseAsset.symbol); + + const handleMarketSelect = ( + marketRef: AtomRef.AtomRef, + ) => { + setSelectedMarket(marketRef); + setIsOpen(false); + }; + + return ( +
+ {/* Asset Selector */} + + +
+ + + {market.baseAsset.symbol} + +
+ + + Perp + +
+ + + + + + + +
+ + {/* Divider */} +
+ + {/* Price */} +
+ + Price + + + {formatAmount(market.markPrice)} + +
+ + {/* 24H Change */} +
+ + 24H Change + + + {isPositiveChange ? "+" : ""} + {formatAmount(market.priceChange24h)} / {isPositiveChange ? "+" : ""} + {formatPercentage(market.priceChangePercent24h)} + +
+ + {/* 24H Volume */} +
+ + 24H Volume + + + {formatCompactUsdAmount(market.volume24h)} USDC + +
+ + {/* Open Interest */} +
+ + Open Interest + + + {formatCompactUsdAmount(market.openInterest)} USDC + +
+ + {/* Maker Fee */} +
+ + Maker Fee + + + {market.makerFee ? formatRate(market.makerFee) : "-"} + +
+ + {/* Taker Fee */} +
+ + Taker Fee + + + {market.takerFee ? formatRate(market.takerFee) : "-"} + +
+
+ ); +} + +export function MarketInfoBar({ className }: MarketInfoBarProps) { + const marketResult = useAtomValue(selectedMarketAtom); + + if (Result.isInitial(marketResult) || Result.isWaiting(marketResult)) { + return ; + } + + if (!Result.isSuccess(marketResult)) { + return ; + } + + return ( + + ); +} diff --git a/packages/dashboard/src/components/modules/trade/market-info/market-selector-popover.tsx b/packages/dashboard/src/components/modules/trade/market-info/market-selector-popover.tsx new file mode 100644 index 0000000..243f9b1 --- /dev/null +++ b/packages/dashboard/src/components/modules/trade/market-info/market-selector-popover.tsx @@ -0,0 +1,248 @@ +import { + type AtomRef, + Result, + useAtomRef, + useAtomValue, +} from "@effect-atom/atom-react"; +import { useVirtualizer } from "@tanstack/react-virtual"; +import { marketsAtom } from "@yieldxyz/perps-common/atoms"; +import { Skeleton, Text, TokenIcon } from "@yieldxyz/perps-common/components"; +import { + cn, + formatAmount, + formatCompactUsdAmount, + formatPercentage, + formatRate, + getMaxLeverage, + getTokenLogo, +} from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; +import { Array as _Array, Option, Record } from "effect"; +import { Search, X } from "lucide-react"; +import { useRef, useState } from "react"; + +interface MarketSelectorContentProps { + onSelect: (marketRef: AtomRef.AtomRef) => void; +} + +interface MarketRowProps { + marketRef: AtomRef.AtomRef; + onSelect: (marketRef: AtomRef.AtomRef) => void; +} + +function MarketRow({ marketRef, onSelect }: MarketRowProps) { + const market = useAtomRef(marketRef); + const isPositiveChange = market.priceChangePercent24h >= 0; + const isPositiveFunding = Number(market.fundingRate) >= 0; + const maxLeverage = getMaxLeverage(market.leverageRange); + const logo = getTokenLogo(market.baseAsset.symbol); + + return ( + + ); +} + +function MarketRowSkeleton() { + return ( +
+
+ + + +
+ + + + + +
+ ); +} + +export function MarketSelectorContent({ + onSelect, +}: MarketSelectorContentProps) { + const [searchQuery, setSearchQuery] = useState(""); + const parentRef = useRef(null); + + const markets = useAtomValue(marketsAtom); + + const isLoading = Result.isInitial(markets) || Result.isWaiting(markets); + + const marketData = markets.pipe( + Result.map(Record.values), + Result.map((v) => + searchQuery.trim() + ? v.filter((market) => + market.value.baseAsset.symbol + .toLowerCase() + .includes(searchQuery.toLowerCase()), + ) + : v, + ), + Result.getOrElse(() => []), + ); + + const rowVirtualizer = useVirtualizer({ + count: marketData.length, + getScrollElement: () => parentRef.current, + estimateSize: () => 44, + overscan: 5, + }); + + const hasNoResults = + marketData.length === 0 && + searchQuery.trim() !== "" && + !Result.isInitial(markets); + + return ( +
+ {/* Search input */} +
+
+ + setSearchQuery(e.target.value)} + placeholder="Search" + className="flex-1 bg-transparent border-none outline-none text-sm text-white placeholder:text-gray-2" + /> + {searchQuery && ( + + )} +
+
+ + {/* Table header */} +
+ Symbol + Last Price + 24H Change + 8H Funding + Volume + Open Interest +
+ + {/* Empty state */} + {hasNoResults ? ( +
+ + + No markets found + + + Try searching for a different symbol + +
+ ) : isLoading ? ( +
+ + + + + +
+ ) : ( + /* Market list with virtualization */ +
+
+ {rowVirtualizer.getVirtualItems().map((virtualItem) => { + const market = _Array + .get(marketData, virtualItem.index) + .pipe(Option.getOrThrow); + + return ( +
+ +
+ ); + })} +
+
+ )} +
+ ); +} diff --git a/packages/dashboard/src/components/modules/trade/order-form/index.tsx b/packages/dashboard/src/components/modules/trade/order-form/index.tsx new file mode 100644 index 0000000..d38510f --- /dev/null +++ b/packages/dashboard/src/components/modules/trade/order-form/index.tsx @@ -0,0 +1,542 @@ +import { + type AtomRef, + Result, + useAtomRef, + useAtomValue, +} from "@effect-atom/atom-react"; +import { walletAtom } from "@yieldxyz/perps-common/atoms"; +import { + Button, + LeverageDialog, + LimitPriceDialog, + PercentageSlider, + Text, + TPOrSLDialog, +} from "@yieldxyz/perps-common/components"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { + cn, + formatAmount, + formatTPOrSLSettings, + getMaxLeverage, + round, +} from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; +import { Schema } from "effect"; +import { ChevronDown, Info } from "lucide-react"; +import { selectedMarketAtom } from "../../../../atoms/selected-market-atom"; +import { + formAtom, + LeverageRangesSchema, + useCurrentPosition, + useLeverage, + useLimitPrice, + useOrderSide, + useOrderType, + useProviderBalance, + useTPOrSLSettings, +} from "./state"; + +interface OrderFormProps { + className?: string; +} + +export function OrderForm({ className }: OrderFormProps) { + const wallet = useAtomValue(walletAtom).pipe(Result.getOrElse(() => null)); + const marketResult = useAtomValue(selectedMarketAtom); + + if (!Result.isSuccess(marketResult)) { + return ; + } + + if (!isWalletConnected(wallet)) { + return ( + + ); + } + + return ( + + ); +} + +function OrderFormLoading({ className }: { className?: string }) { + return ( +
+ Loading market... +
+ ); +} + +function OrderFormDisconnected({ + className, + marketRef, +}: { + className?: string; + marketRef: AtomRef.AtomRef; +}) { + const market = useAtomRef(marketRef); + const { orderType, setOrderType } = useOrderType(); + const { orderSide, setOrderSide } = useOrderSide(); + + const leverageRanges = Schema.decodeSync(LeverageRangesSchema)( + market.leverageRange, + ); + const { leverage } = useLeverage(leverageRanges); + const { tpOrSLSettings } = useTPOrSLSettings(); + + const maxLeverage = getMaxLeverage(leverageRanges); + + return ( +
+ {/* Market/Limit Tabs */} + + +
+ {/* Buy/Sell Toggle */} + + + {/* Available to Trade & Current Position */} +
+
+ + Available to Trade + + + -- + +
+
+ + Current Position + + -- +
+
+ + {/* Amount Display */} +
+ + $0 + + + 0.000000 {market.baseAsset.symbol} + +
+ + {/* Percentage Slider (disabled) */} + {}} /> + + {/* Leverage */} + + + {/* Limit Price (when limit order) */} + {orderType === "limit" && ( + + )} + + {/* Advanced Orders */} + +
+ + {/* Order Details */} +
+ + + +
+ + {/* Connect Wallet Button */} +
+ +
+
+ ); +} + +function OrderFormContent({ + className, + wallet, + marketRef, +}: { + className?: string; + wallet: WalletConnected; + marketRef: AtomRef.AtomRef; +}) { + const market = useAtomRef(marketRef); + const leverageRanges = Schema.decodeSync(LeverageRangesSchema)( + market.leverageRange, + ); + + const { orderType, setOrderType } = useOrderType(); + const { orderSide, setOrderSide } = useOrderSide(); + const { setLeverage } = useLeverage(leverageRanges); + const { tpOrSLSettings, setTPOrSLSettings } = useTPOrSLSettings(); + const { limitPrice, setLimitPrice } = useLimitPrice(); + const { providerBalance } = useProviderBalance(wallet); + const { currentPosition } = useCurrentPosition(wallet, market.id); + + const { + hooks: { + useOrderForm, + useOrderPercentage, + useOrderCalculations, + useHandlePercentageChange, + }, + form: OrderFormComponent, + } = useAtomValue(formAtom(leverageRanges)); + + const { submit, submitResult } = useOrderForm(); + const { handlePercentageChange } = useHandlePercentageChange(wallet); + const { percentage } = useOrderPercentage(wallet, leverageRanges); + const calculations = useOrderCalculations(market, orderSide); + + const maxLeverage = getMaxLeverage(leverageRanges); + const currentPrice = market.markPrice; + const symbol = market.baseAsset.symbol; + + const availableBalance = providerBalance?.availableBalance ?? 0; + + // Format current position display + const currentPositionDisplay = currentPosition + ? `${currentPosition.side === "long" ? "+" : "-"}${currentPosition.size} ${symbol}` + : "--"; + + const handleSubmit = () => { + submit({ wallet, market, side: orderSide }); + }; + + return ( +
+ {/* Market/Limit Tabs */} + + +
+ {/* Buy/Sell Toggle */} + + + {/* Available to Trade & Current Position */} +
+
+ + Available to Trade + + + {formatAmount(availableBalance)} + +
+
+ + Current Position + + + {currentPositionDisplay} + +
+
+ + {/* Amount Input */} + +
+ + + {round(calculations.cryptoAmount, 6).toString()} {symbol} + +
+
+ + {/* Percentage Slider */} + + + {/* Leverage */} + + + + + {/* Limit Price (when limit order) */} + {orderType === "limit" && ( + + + + )} + + {/* Advanced Orders (TP/SL) */} + + + +
+ + {/* Order Details */} +
+ + + +
+ + {/* Place Order Button */} +
+ +
+
+ ); +} + +// Shared Components + +function OrderTypeTabs({ + orderType, + setOrderType, +}: { + orderType: "market" | "limit"; + setOrderType: (type: "market" | "limit") => void; +}) { + return ( +
+ + +
+ ); +} + +function OrderSideToggle({ + orderSide, + setOrderSide, +}: { + orderSide: "long" | "short"; + setOrderSide: (side: "long" | "short") => void; +}) { + return ( +
+ + +
+ ); +} + +function SettingsRow({ + label, + value, + maxLeverage, +}: { + label: string; + value: string; + maxLeverage?: number; +}) { + return ( +
+
+ {label} + +
+
+ {value} + {maxLeverage && ( + / {maxLeverage}x + )} +
+
+ ); +} + +interface OrderDetailRowProps { + label: string; + value: string; + isLast?: boolean; +} + +function OrderDetailRow({ label, value, isLast }: OrderDetailRowProps) { + return ( +
+ + {label} + + {value} +
+ ); +} diff --git a/packages/dashboard/src/components/modules/trade/order-form/sign-dialog.tsx b/packages/dashboard/src/components/modules/trade/order-form/sign-dialog.tsx new file mode 100644 index 0000000..20fac08 --- /dev/null +++ b/packages/dashboard/src/components/modules/trade/order-form/sign-dialog.tsx @@ -0,0 +1,86 @@ +import { Result, useAtomSet, useAtomValue } from "@effect-atom/atom-react"; +import { + actionAtom, + signActionAtoms, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { + Dialog, + Skeleton, + TransactionProgress, +} from "@yieldxyz/perps-common/components"; +import { isWalletConnected } from "@yieldxyz/perps-common/domain"; + +export function SignTransactionsDialog() { + const wallet = useAtomValue(walletAtom).pipe(Result.getOrElse(() => null)); + const action = useAtomValue(actionAtom); + const setAction = useAtomSet(actionAtom); + + const isOpen = action !== null && isWalletConnected(wallet); + + const handleClose = () => { + setAction(null); + }; + + if (!isWalletConnected(wallet)) { + return null; + } + + const machineAtoms = signActionAtoms(wallet.signTransactions); + + return ( + !open && handleClose()}> + + + + + + Transaction Progress + + + + + + + + ); +} + +interface SignTransactionsContentProps { + machineAtoms: ReturnType; + onClose: () => void; +} + +function SignTransactionsContent({ + machineAtoms, + onClose, +}: SignTransactionsContentProps) { + const { machineStreamAtom, retryMachineAtom } = machineAtoms; + const state = useAtomValue(machineStreamAtom); + const retry = useAtomSet(retryMachineAtom); + + const result = Result.all({ state, retry }); + + if (Result.isFailure(result)) { + return ( +
+ +
+ ); + } + + if (Result.isSuccess(result)) { + return ( + + ); + } + + return ; +} diff --git a/packages/dashboard/src/components/modules/trade/order-form/state.tsx b/packages/dashboard/src/components/modules/trade/order-form/state.tsx new file mode 100644 index 0000000..c3d79bf --- /dev/null +++ b/packages/dashboard/src/components/modules/trade/order-form/state.tsx @@ -0,0 +1,419 @@ +import { + Atom, + Registry, + Result, + useAtomSet, + useAtomValue, +} from "@effect-atom/atom-react"; +import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; +import { + actionAtom, + positionsAtom, + selectedProviderAtom, + selectedProviderBalancesAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { + AmountField, + type TPOrSLSettings, +} from "@yieldxyz/perps-common/components"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { + calcBaseAmountFromUsd, + calculateMargin, + calculatePositionSize, + clampPercent, + getLiquidationPrice, + getMaxLeverage, + MIN_LEVERAGE, + percentOf, + round, + valueFromPercent, +} from "@yieldxyz/perps-common/lib"; +import { + ApiClientService, + ApiSchemas, + type ApiTypes, + runtimeAtom, +} from "@yieldxyz/perps-common/services"; +import { Number as _Number, Effect, Option, Schema } from "effect"; + +export type OrderType = "market" | "limit"; +export type OrderSide = ApiTypes.PositionDtoSide; + +export const LeverageRangesSchema = Schema.Data( + ApiSchemas.MarketDto.fields.leverageRange, +).pipe(Schema.brand("LeverageRange")); + +// Order Type Atom +const orderTypeAtom = Atom.writable( + () => "market", + (ctx, value) => ctx.setSelf(value), +); + +// Order Side Atom +const orderSideAtom = Atom.writable( + () => "long", + (ctx, value) => ctx.setSelf(value), +); + +// Leverage Atom (family keyed by leverage ranges) +const leverageAtom = Atom.family( + (leverageRanges: typeof LeverageRangesSchema.Type) => + Atom.writable( + () => getMaxLeverage(leverageRanges), + (ctx, value: number) => + ctx.setSelf( + _Number.clamp({ + minimum: MIN_LEVERAGE, + maximum: getMaxLeverage(leverageRanges), + })(value), + ), + ), +); + +// TP/SL Settings Atom +const tpOrSLSettingsAtom = Atom.writable( + () => ({ + takeProfit: { + option: null, + triggerPrice: null, + percentage: null, + }, + stopLoss: { + option: null, + triggerPrice: null, + percentage: null, + }, + }), + (ctx, value) => ctx.setSelf(value), +); + +// Limit Price Atom +const limitPriceAtom = Atom.writable( + () => null, + (ctx, value) => ctx.setSelf(value), +); + +// Hooks +export const useOrderType = () => { + const orderType = useAtomValue(orderTypeAtom); + const setOrderType = useAtomSet(orderTypeAtom); + + return { + orderType, + setOrderType, + }; +}; + +export const useOrderSide = () => { + const orderSide = useAtomValue(orderSideAtom); + const setOrderSide = useAtomSet(orderSideAtom); + + return { + orderSide, + setOrderSide, + }; +}; + +export const useLeverage = ( + leverageRanges: typeof LeverageRangesSchema.Type, +) => { + const leverage = useAtomValue(leverageAtom(leverageRanges)); + const setLeverage = useAtomSet(leverageAtom(leverageRanges)); + + return { + leverage, + setLeverage, + }; +}; + +export const useTPOrSLSettings = () => { + const tpOrSLSettings = useAtomValue(tpOrSLSettingsAtom); + const setTPOrSLSettings = useAtomSet(tpOrSLSettingsAtom); + + return { + tpOrSLSettings, + setTPOrSLSettings, + }; +}; + +export const useLimitPrice = () => { + const limitPrice = useAtomValue(limitPriceAtom); + const setLimitPrice = useAtomSet(limitPriceAtom); + + return { + limitPrice, + setLimitPrice, + }; +}; + +export const useProviderBalance = (wallet: WalletConnected) => { + const providerBalance = useAtomValue( + selectedProviderBalancesAtom(wallet.currentAccount.address), + ).pipe(Result.getOrElse(() => null)); + + return { + providerBalance, + }; +}; + +export const useCurrentPosition = ( + wallet: WalletConnected, + marketId: string, +) => { + const positions = useAtomValue( + positionsAtom(wallet.currentAccount.address), + ).pipe(Result.getOrElse(() => [])); + + const currentPosition = positions.find( + (position) => position.marketId === marketId, + ); + + return { + currentPosition: currentPosition ?? null, + }; +}; + +// Form Atom +export const formAtom = Atom.family( + (leverageRanges: typeof LeverageRangesSchema.Type) => { + const orderFormBuilder = FormBuilder.empty + .addField( + "Amount", + Schema.NumberFromString.pipe( + Schema.annotations({ message: () => "Invalid amount" }), + Schema.greaterThan(0, { message: () => "Must be greater than 0" }), + ), + ) + .refineEffect((values) => + Effect.gen(function* () { + const registry = yield* Registry.AtomRegistry; + const wallet = registry + .get(walletAtom) + .pipe(Result.getOrElse(() => null)); + + if (!isWalletConnected(wallet)) { + return yield* Effect.dieMessage("No wallet"); + } + + const providerBalance = registry + .get(selectedProviderBalancesAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!providerBalance) { + return { path: ["Amount"], message: "Missing provider balance" }; + } + + const leverage = registry.get(leverageAtom(leverageRanges)); + const requiredMargin = calculateMargin({ + positionSize: values.Amount, + leverage, + }); + + if (requiredMargin > providerBalance.availableBalance) { + return { + path: ["Amount"], + message: "Insufficient balance", + }; + } + }), + ); + + const OrderForm = FormReact.make(orderFormBuilder, { + runtime: runtimeAtom, + fields: { Amount: AmountField }, + onSubmit: ( + { + wallet, + market, + side, + }: { + wallet: WalletConnected; + market: ApiSchemas.MarketDto; + side: ApiTypes.PositionDtoSide; + }, + { decoded }, + ) => + Effect.gen(function* () { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + const leverage = registry.get(leverageAtom(leverageRanges)); + const orderType = registry.get(orderTypeAtom); + const tpOrSLSettings = registry.get(tpOrSLSettingsAtom); + + const stopLossPrice: ApiTypes.ArgumentsDto["stopLossPrice"] = + tpOrSLSettings.stopLoss.triggerPrice && + tpOrSLSettings.stopLoss.option !== null + ? tpOrSLSettings.stopLoss.triggerPrice + : undefined; + + const takeProfitPrice: ApiTypes.ArgumentsDto["takeProfitPrice"] = + tpOrSLSettings.takeProfit.triggerPrice && + tpOrSLSettings.takeProfit.option !== null + ? tpOrSLSettings.takeProfit.triggerPrice + : undefined; + + const limitPrice = + orderType === "limit" ? registry.get(limitPriceAtom) : undefined; + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: wallet.currentAccount.address, + action: "open", + args: { + marketId: market.id, + side, + size: decoded.Amount.toString(), + marginMode: "isolated", + ...(stopLossPrice && { stopLossPrice }), + ...(takeProfitPrice && { takeProfitPrice }), + ...(leverage && { leverage }), + ...(limitPrice && { limitPrice: limitPrice }), + }, + }); + + registry.set(actionAtom, action); + }), + }); + + const useOrderForm = () => { + const submit = useAtomSet(OrderForm.submit); + const submitResult = useAtomValue(OrderForm.submit); + + return { + submit, + submitResult, + }; + }; + + const setAmountFieldAtom = OrderForm.setValue(OrderForm.fields.Amount); + const amountFieldAtom = OrderForm.getFieldValue(OrderForm.fields.Amount); + + const useHandlePercentageChange = (wallet: WalletConnected) => { + const setAmount = useAtomSet(setAmountFieldAtom); + const { providerBalance } = useProviderBalance(wallet); + const { leverage } = useLeverage(leverageRanges); + + return { + handlePercentageChange: (value: number) => { + if (!providerBalance) return; + + const clampedValue = clampPercent(value); + const marginToUse = valueFromPercent({ + total: providerBalance.availableBalance, + percent: clampedValue, + }); + const positionSize = calculatePositionSize({ + margin: marginToUse, + leverage, + }); + + setAmount(round(positionSize).toString()); + }, + }; + }; + + const useOrderAmount = () => { + const amount = useAtomValue(amountFieldAtom).pipe( + Option.map(Number), + Option.filter((v) => !Number.isNaN(v)), + Option.getOrElse(() => 0), + ); + + return { amount }; + }; + + const useOrderPercentage = ( + wallet: WalletConnected, + leverageRanges: typeof LeverageRangesSchema.Type, + ) => { + const amount = useAtomValue(amountFieldAtom).pipe( + Option.map(Number), + Option.filter((v) => !Number.isNaN(v)), + Option.getOrElse(() => 0), + ); + + const { leverage } = useLeverage(leverageRanges); + + const providerBalance = useAtomValue( + selectedProviderBalancesAtom(wallet.currentAccount.address), + ).pipe(Result.getOrElse(() => null)); + + if (!providerBalance) { + return { + percentage: 0, + }; + } + + const margin = calculateMargin({ positionSize: amount, leverage }); + const percentage = clampPercent( + percentOf({ part: margin, whole: providerBalance.availableBalance }), + ); + + return { + percentage: Math.round(percentage), + }; + }; + + const useOrderCalculations = ( + market: ApiSchemas.MarketDto, + side: ApiTypes.PositionDtoSide, + ) => { + const { amount } = useOrderAmount(); + const { leverage } = useLeverage( + Schema.decodeSync(LeverageRangesSchema)(market.leverageRange), + ); + + const cryptoAmount = calcBaseAmountFromUsd({ + usdAmount: amount, + priceUsd: market.markPrice, + }); + + const liquidationPrice = getLiquidationPrice({ + currentPrice: market.markPrice, + leverage, + side, + }); + + const margin = calculateMargin({ positionSize: amount, leverage }); + + const feeRate = market.takerFee ? Number.parseFloat(market.takerFee) : 0; + const fees = amount * feeRate; + + return { + margin, + amount, + cryptoAmount, + liquidationPrice, + fees, + leverage, + }; + }; + + const hooks = { + useOrderForm, + useOrderAmount, + useOrderPercentage, + useOrderCalculations, + useHandlePercentageChange, + }; + + return Atom.readable(() => ({ + hooks, + form: OrderForm, + })); + }, +); diff --git a/packages/dashboard/src/components/molecules/header/deposit/deposit-dialog.tsx b/packages/dashboard/src/components/molecules/header/deposit/deposit-dialog.tsx new file mode 100644 index 0000000..23ef27c --- /dev/null +++ b/packages/dashboard/src/components/molecules/header/deposit/deposit-dialog.tsx @@ -0,0 +1,360 @@ +import { Result, useAtomValue } from "@effect-atom/atom-react"; +import hyperliquidLogo from "@yieldxyz/perps-common/assets/hyperliquid.png"; +import { + providersAtom, + yieldApiNetworkToMoralisChain, +} from "@yieldxyz/perps-common/atoms"; +import { + Button, + Dialog, + Select, + Text, + TokenIcon, +} from "@yieldxyz/perps-common/components"; +import type { + WalletAccount, + WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { + formatSnakeCase, + formatTokenAmount, + getNetworkLogo, +} from "@yieldxyz/perps-common/lib"; +import type { ApiSchemas } from "@yieldxyz/perps-common/services"; +import { Array as _Array, Option, Record } from "effect"; +import { + DepositForm, + useDepositForm, + useProviders, + useSelectedChain, + useSelectedTokenBalance, + useTokenBalances, +} from "./state"; + +interface DepositDialogProps { + wallet: WalletConnected; + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export function DepositDialog({ + wallet, + open, + onOpenChange, +}: DepositDialogProps) { + return ( + + + + + + + Deposit funds + + + + + + + + ); +} + +interface DepositDialogContentProps { + wallet: WalletConnected; +} + +function DepositDialogContent({ wallet }: DepositDialogContentProps) { + const { submit, submitResult } = useDepositForm(); + + const handleSubmit = () => { + submit({ wallet }); + }; + + return ( + +
+
+ {/* Provider Select */} + + + {/* Chain and Token Selects */} +
+
+ +
+
+ +
+
+ + {/* Amount Input */} + +
+ + {/* Deposit Button */} + +
+
+ ); +} + +function ProviderSelect() { + const providers = useAtomValue(providersAtom).pipe( + Result.getOrElse(() => [] as ReadonlyArray), + ); + const { selectedProvider, setSelectedProvider } = useProviders(); + + const items = providers.map((provider) => ({ + value: provider.id, + label: provider.name, + provider, + })); + + return ( +
+ + Select provider + + { + if (!value) return; + const provider = providers.find((p) => p.id === value); + if (provider) setSelectedProvider(provider); + }} + > + +
+ {selectedProvider?.name + + {selectedProvider?.name ?? "Select provider"} + +
+
+ + + + {items.map((item) => ( + + } + > + {item.label} + + ))} + + + +
+
+ ); +} + +interface ChainSelectProps { + walletAddress: WalletAccount["address"]; +} + +function ChainSelect({ walletAddress }: ChainSelectProps) { + const { selectedChain, setSelectedChain } = useSelectedChain(); + const { handleSelectTokenBalance } = useSelectedTokenBalance(walletAddress); + const { tokenBalances } = useTokenBalances(walletAddress); + + const chains = Record.keys(yieldApiNetworkToMoralisChain); + + const handleChainChange = (value: string | null) => { + if (!value) return; + const chain = value as keyof typeof yieldApiNetworkToMoralisChain; + setSelectedChain(chain); + // Select first token of the new chain + const chainTokens = tokenBalances[chain] ?? []; + const firstToken = _Array.head(chainTokens); + if (Option.isSome(firstToken)) { + handleSelectTokenBalance(firstToken.value); + } + }; + + return ( +
+ + Select chain + + + +
+ + + {formatSnakeCase(selectedChain)} + +
+
+ + + + {chains.map((chain) => ( + + } + > + {formatSnakeCase(chain)} + + ))} + + + +
+
+ ); +} + +interface TokenSelectProps { + walletAddress: WalletAccount["address"]; +} + +function TokenSelect({ walletAddress }: TokenSelectProps) { + const { selectedChain } = useSelectedChain(); + const { selectedTokenBalance, handleSelectTokenBalance } = + useSelectedTokenBalance(walletAddress); + const { tokenBalances } = useTokenBalances(walletAddress); + + const chainTokens = tokenBalances[selectedChain] ?? []; + + return ( +
+ + Select token + + { + if (!value) return; + const token = chainTokens.find( + (t) => `${t.token.network}-${t.token.address}` === value, + ); + if (token) handleSelectTokenBalance(token); + }} + > + +
+ {selectedTokenBalance?.token.logoURI && ( + + )} + + {selectedTokenBalance?.token.symbol ?? "Select token"} + +
+
+ + + + {chainTokens.map((tokenBalance) => ( + + ) : undefined + } + > +
+ {tokenBalance.token.symbol} + + {formatTokenAmount({ + amount: tokenBalance.amount, + symbol: tokenBalance.token.symbol, + })} + +
+
+ ))} +
+
+
+
+
+ ); +} + +interface AmountInputProps { + walletAddress: WalletAccount["address"]; +} + +function AmountInput({ walletAddress }: AmountInputProps) { + const { selectedTokenBalance } = useSelectedTokenBalance(walletAddress); + + const availableBalance = selectedTokenBalance + ? formatTokenAmount({ + amount: selectedTokenBalance.amount, + symbol: selectedTokenBalance.token.symbol, + }) + : null; + + return ( +
+
+ + Enter amount + + {availableBalance && ( + + Available: {availableBalance} + + )} +
+ +
+ ); +} diff --git a/packages/dashboard/src/components/molecules/header/deposit/state.tsx b/packages/dashboard/src/components/molecules/header/deposit/state.tsx new file mode 100644 index 0000000..c02700d --- /dev/null +++ b/packages/dashboard/src/components/molecules/header/deposit/state.tsx @@ -0,0 +1,381 @@ +import { + Atom, + Registry, + Result, + useAtomSet, + useAtomValue, +} from "@effect-atom/atom-react"; +import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; +import { + actionAtom, + moralisTokenBalancesAtom, + selectedProviderAtom, + type TokenBalances, + walletAtom, + type yieldApiNetworkToMoralisChain, +} from "@yieldxyz/perps-common/atoms"; +import { Text } from "@yieldxyz/perps-common/components"; +import { + isArbUsdcToken, + isEthNativeToken, + isWalletConnected, + makeToken, + type TokenBalance, + type WalletAccount, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { + calcBaseAmountFromUsd, + clampPercent, + formatTokenAmount, + percentOf, + round, + valueFromPercent, +} from "@yieldxyz/perps-common/lib"; +import { + ApiClientService, + type ApiTypes, + runtimeAtom, +} from "@yieldxyz/perps-common/services"; +import { + Array as _Array, + Effect, + Option, + Predicate, + Record, + Schema, +} from "effect"; + +// Selected chain atom (network) +type ChainKey = keyof typeof yieldApiNetworkToMoralisChain; + +const initialChainAtom = Atom.make("ethereum"); + +export const selectedChainAtom = Atom.writable( + (ctx) => ctx.get(initialChainAtom), + (_ctx, value: ChainKey) => _ctx.setSelf(value), +); + +// Selected token balance atom based on wallet address +const selectedTokenBalanceAtom = Atom.family( + (walletAddress: WalletAccount["address"]) => + Atom.writable( + (ctx) => + ctx.get(moralisTokenBalancesAtom(walletAddress)).pipe( + Result.map((res) => _Array.head(res.ethereum)), + Result.map(Option.getOrNull), + ), + (ctx, value: TokenBalance) => ctx.setSelf(Result.success(value)), + ), +); + +// Hooks for using atoms in components +export const useProviders = (): { + selectedProvider: ApiTypes.ProviderDto | null; + setSelectedProvider: (value: ApiTypes.ProviderDto) => void; +} => { + const selectedProvider = useAtomValue(selectedProviderAtom).pipe( + Result.getOrElse(() => null), + ); + const setSelectedProvider = useAtomSet(selectedProviderAtom); + + return { + selectedProvider, + setSelectedProvider, + }; +}; + +export const useSelectedChain = () => { + const selectedChain = useAtomValue(selectedChainAtom); + const setSelectedChain = useAtomSet(selectedChainAtom); + + return { + selectedChain, + setSelectedChain, + }; +}; + +export const useTokenBalances = (walletAddress: WalletAccount["address"]) => { + const tokenBalances = useAtomValue( + moralisTokenBalancesAtom(walletAddress), + ).pipe(Result.getOrElse(() => Record.empty() as TokenBalances)); + + return { + tokenBalances, + }; +}; + +export const useSelectedTokenBalance = ( + walletAddress: WalletAccount["address"], +) => { + const selectedTokenBalance = useAtomValue( + selectedTokenBalanceAtom(walletAddress), + ).pipe(Result.getOrElse(() => null)); + const setSelectedTokenBalance = useAtomSet( + selectedTokenBalanceAtom(walletAddress), + ); + const setAmount = useAtomSet(setAmountFieldAtom); + + const handleSelectTokenBalance = (tokenBalance: TokenBalance) => { + setSelectedTokenBalance(tokenBalance); + setAmount("0"); + }; + + return { + selectedTokenBalance, + handleSelectTokenBalance, + }; +}; + +// Form builder for deposit +export const depositFormBuilder = FormBuilder.empty + .addField( + "Amount", + Schema.NumberFromString.pipe( + Schema.annotations({ message: () => "Invalid amount" }), + Schema.greaterThan(0, { message: () => "Must be greater than 0" }), + ), + ) + .refineEffect((values) => + Effect.gen(function* () { + const registry = yield* Registry.AtomRegistry; + const wallet = registry + .get(walletAtom) + .pipe(Result.getOrElse(() => null)); + + if (!isWalletConnected(wallet)) { + yield* Effect.logWarning("No wallet found"); + return; + } + + const tokenBalance = registry + .get(selectedTokenBalanceAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!tokenBalance) { + return { path: ["Amount"], message: "Missing token balance" }; + } + + const cryptoAmount = calcBaseAmountFromUsd({ + usdAmount: values.Amount, + priceUsd: tokenBalance.price, + }); + + const usdMin = isArbUsdcToken(makeToken(tokenBalance.token)) ? 5 : 10; + + if (values.Amount < usdMin) { + return { path: ["Amount"], message: `Minimum deposit is $${usdMin}` }; + } + + if (Number(tokenBalance.amount) < cryptoAmount) { + return { path: ["Amount"], message: "Insufficient balance" }; + } + }), + ); + +// Custom amount field component that matches the Figma design +const DepositAmountField: FormReact.FieldComponent = ({ field }) => { + const onChange: (typeof field)["onChange"] = (newValue) => { + const value = newValue.replace(/[^0-9.,]/g, ""); + const parts = value.split(/[.,]/); + if (parts.length > 2) return; + field.onChange(value); + }; + + return ( +
+
+ { + if (field.value === "0") { + onChange(""); + } + }} + onBlurCapture={() => { + if (field.value === "" || field.value.startsWith("00")) { + onChange("0"); + } + }} + onChange={(e) => onChange(e.target.value)} + autoComplete="off" + autoCorrect="off" + spellCheck="false" + pattern="^(?!0\d)\d*([.,])?(\d+)?$" + minLength={1} + maxLength={79} + onBlur={field.onBlur} + placeholder="0.00" + className="h-10 text-white text-sm font-semibold tracking-[-0.42px] leading-[1.25] bg-transparent border-none outline-none placeholder:text-gray-4 w-full caret-accent-green" + /> +
+ {Option.isSome(field.error) && ( +
+
+ + ! + +
+ + {field.error.value} + +
+ )} +
+ ); +}; + +export const DepositForm = FormReact.make(depositFormBuilder, { + runtime: runtimeAtom, + fields: { Amount: DepositAmountField }, + onSubmit: ({ wallet }: { wallet: WalletConnected }, { decoded }) => + Effect.gen(function* () { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const selectedTokenBalance = registry + .get(selectedTokenBalanceAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!selectedTokenBalance) { + return yield* Effect.dieMessage("No selected token balance"); + } + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + const cryptoAmount = calcBaseAmountFromUsd({ + usdAmount: decoded.Amount, + priceUsd: selectedTokenBalance.price, + }); + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: wallet.currentAccount.address, + action: "fund", + args: { + amount: cryptoAmount.toString(), + fromToken: { + network: selectedTokenBalance.token.network, + ...(!isEthNativeToken(makeToken(selectedTokenBalance.token)) && { + address: selectedTokenBalance.token.address, + }), + }, + }, + }); + + registry.set(actionAtom, action); + }), +}); + +const amountAtom = DepositForm.getFieldValue(DepositForm.fields.Amount); +const setAmountFieldAtom = DepositForm.setValue(DepositForm.fields.Amount); + +export const useDepositForm = () => { + const submit = useAtomSet(DepositForm.submit); + const submitResult = useAtomValue(DepositForm.submit); + + return { + submit, + submitResult, + }; +}; + +export const useDepositPercentage = ( + walletAddress: WalletAccount["address"], +) => { + const amount = useAtomValue(amountAtom).pipe( + Option.map(Number), + Option.filter((v) => !Number.isNaN(v)), + Option.getOrElse(() => 0), + ); + + const setAmount = useAtomSet(setAmountFieldAtom); + + const availableBalanceUsd = useAtomValue( + selectedTokenBalanceAtom(walletAddress), + ).pipe( + Result.value, + Option.filter(Predicate.isNotNull), + Option.map((balance) => Number(balance.amount) * balance.price), + Option.getOrElse(() => 0), + ); + + const percentage = clampPercent( + percentOf({ part: amount, whole: availableBalanceUsd }), + ); + + const handlePercentageChange = (newPercentage: number) => { + if (newPercentage >= 100) { + return setAmount(availableBalanceUsd.toString()); + } + + const amount = valueFromPercent({ + total: availableBalanceUsd, + percent: newPercentage, + }); + setAmount(round(amount).toString()); + }; + + return { + handlePercentageChange, + percentage: Math.round(percentage), + }; +}; + +const tokenAmountValueAtom = Atom.family( + (walletAddress: WalletAccount["address"]) => + runtimeAtom.atom((ctx) => + Effect.gen(function* () { + const tokenBalance = yield* ctx.result( + selectedTokenBalanceAtom(walletAddress), + ); + + ctx.subscribe(amountAtom, () => {}); + + const amount = ctx.get(amountAtom).pipe( + Option.map(parseFloat), + Option.filter((v) => !Number.isNaN(v)), + Option.getOrElse(() => 0), + ); + + if (!tokenBalance) { + return ""; + } + + return formatTokenAmount({ + amount: calcBaseAmountFromUsd({ + usdAmount: amount, + priceUsd: tokenBalance.price, + }), + symbol: tokenBalance.token.symbol, + }); + }), + ), +); + +export const useTokenAmountValue = ( + walletAddress: WalletAccount["address"], +) => { + const tokenAmountValue = useAtomValue( + tokenAmountValueAtom(walletAddress), + ).pipe(Result.getOrElse(() => null)); + + return { + tokenAmountValue, + }; +}; diff --git a/packages/dashboard/src/components/molecules/header/index.tsx b/packages/dashboard/src/components/molecules/header/index.tsx new file mode 100644 index 0000000..4c54a2c --- /dev/null +++ b/packages/dashboard/src/components/molecules/header/index.tsx @@ -0,0 +1,230 @@ +import { Result, useAtomSet, useAtomValue } from "@effect-atom/atom-react"; +import { useAppKit } from "@reown/appkit/react"; +import { Link } from "@tanstack/react-router"; +import hyperliquidLogo from "@yieldxyz/perps-common/assets/hyperliquid.png"; +import { + providersAtom, + selectedProviderAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { + AddressSwitcher, + Button, + Dialog, + Text, +} from "@yieldxyz/perps-common/components"; +import { + isBrowserWallet, + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { cn, truncateAddress } from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; +import { ChevronRight } from "lucide-react"; +import { useState } from "react"; +import { Logo } from "../../atoms/logo"; +import { DepositDialog } from "./deposit/deposit-dialog"; +import { WithdrawDialog } from "./withdraw/withdraw-dialog"; + +interface HeaderProps { + className?: string; +} + +export function Header({ className }: HeaderProps) { + return ( +
+
+ {/* Left side: Logo + Navigation */} +
+ {/* Logo */} + + + + + {/* Navigation */} + +
+ + {/* Right side: Wallet section */} + +
+
+ ); +} + +function HeaderWalletSection() { + const { open } = useAppKit(); + const wallet = useAtomValue(walletAtom).pipe(Result.getOrElse(() => null)); + + const browserWallet = isBrowserWallet(wallet); + const walletConnected = isWalletConnected(wallet); + + // Disconnected state - show Connect button + if (!walletConnected) { + return ( +
+ +
+ ); + } + + // Connected state - show provider selector, address, and deposit button + return ; +} + +function ConnectedWalletSection({ wallet }: { wallet: WalletConnected }) { + const [providerDialogOpen, setProviderDialogOpen] = useState(false); + const [depositDialogOpen, setDepositDialogOpen] = useState(false); + const [withdrawDialogOpen, setWithdrawDialogOpen] = useState(false); + + const providers = useAtomValue(providersAtom).pipe( + Result.getOrElse(() => [] as ReadonlyArray), + ); + const selectedProvider = useAtomValue(selectedProviderAtom).pipe( + Result.getOrElse(() => null), + ); + const setSelectedProvider = useAtomSet(selectedProviderAtom); + + const handleProviderSelect = (provider: ApiTypes.ProviderDto) => { + setSelectedProvider(provider); + setProviderDialogOpen(false); + }; + + return ( +
+ {/* Provider selector */} + + + {/* Provider dialog */} + + + + + + + Select Provider + + +
+ {providers.map((provider) => ( + + ))} +
+
+
+
+
+ + {/* Address display with switcher */} + + + {truncateAddress(wallet.currentAccount.address)} + + + + } + /> + + {/* Withdraw button */} + + + {/* Deposit button */} + + + {/* Withdraw dialog */} + + + {/* Deposit dialog */} + +
+ ); +} diff --git a/packages/dashboard/src/components/molecules/header/withdraw/state.tsx b/packages/dashboard/src/components/molecules/header/withdraw/state.tsx new file mode 100644 index 0000000..b47a63d --- /dev/null +++ b/packages/dashboard/src/components/molecules/header/withdraw/state.tsx @@ -0,0 +1,269 @@ +import { + Atom, + Registry, + Result, + useAtomSet, + useAtomValue, +} from "@effect-atom/atom-react"; +import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; +import { + actionAtom, + providersAtom, + selectedProviderAtom, + selectedProviderBalancesAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { Text } from "@yieldxyz/perps-common/components"; +import { + isWalletConnected, + type WalletAccount, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { + clampPercent, + percentOf, + round, + valueFromPercent, +} from "@yieldxyz/perps-common/lib"; +import { + ApiClientService, + type ApiTypes, + runtimeAtom, +} from "@yieldxyz/perps-common/services"; +import { Array as _Array, Effect, Option, Schema } from "effect"; + +const withdrawSelectedProviderAtom = Atom.writable( + (ctx) => + ctx + .get(providersAtom) + .pipe(Result.map(_Array.head), Result.map(Option.getOrNull)), + (ctx, value: ApiTypes.ProviderDto) => ctx.setSelf(Result.success(value)), +); + +export const useProviders = (): { + selectedProvider: ApiTypes.ProviderDto | null; + setSelectedProvider: (value: ApiTypes.ProviderDto) => void; +} => { + const selectedProvider = useAtomValue(selectedProviderAtom).pipe( + Result.getOrElse(() => null), + ); + const setSelectedProvider = useAtomSet(withdrawSelectedProviderAtom); + + return { + selectedProvider, + setSelectedProvider, + }; +}; + +export const useProviderBalance = (walletAddress: WalletAccount["address"]) => { + const providerBalance = useAtomValue( + selectedProviderBalancesAtom(walletAddress), + ).pipe(Result.getOrElse(() => null)); + + return { + providerBalance, + }; +}; + +export const useWithdrawForm = () => { + const submit = useAtomSet(WithdrawForm.submit); + const submitResult = useAtomValue(WithdrawForm.submit); + + return { + submit, + submitResult, + }; +}; + +export const withdrawFormBuilder = FormBuilder.empty + .addField( + "Amount", + Schema.NumberFromString.pipe( + Schema.annotations({ message: () => "Invalid amount" }), + Schema.greaterThan(0, { message: () => "Must be greater than 0" }), + ), + ) + .refineEffect((values) => + Effect.gen(function* () { + const registry = yield* Registry.AtomRegistry; + const wallet = registry + .get(walletAtom) + .pipe(Result.getOrElse(() => null)); + + if (!isWalletConnected(wallet)) { + return yield* Effect.dieMessage("No wallet"); + } + + const providerBalance = registry + .get(selectedProviderBalancesAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!providerBalance) { + return { path: ["Amount"], message: "Missing provider balance" }; + } + + if (providerBalance.availableBalance <= 0) { + return { + path: ["Amount"], + message: "No available balance to withdraw", + }; + } + + if (values.Amount > providerBalance.availableBalance) { + return { + path: ["Amount"], + message: "Insufficient balance", + }; + } + }), + ); + +// Custom amount field component that matches the deposit dialog design +const WithdrawAmountField: FormReact.FieldComponent = ({ field }) => { + const onChange: (typeof field)["onChange"] = (newValue) => { + const value = newValue.replace(/[^0-9.,]/g, ""); + const parts = value.split(/[.,]/); + if (parts.length > 2) return; + field.onChange(value); + }; + + return ( +
+
+ { + if (field.value === "0") { + onChange(""); + } + }} + onBlurCapture={() => { + if (field.value === "" || field.value.startsWith("00")) { + onChange("0"); + } + }} + onChange={(e) => onChange(e.target.value)} + autoComplete="off" + autoCorrect="off" + spellCheck="false" + pattern="^(?!0\d)\d*([.,])?(\d+)?$" + minLength={1} + maxLength={79} + onBlur={field.onBlur} + placeholder="0.00" + className="h-10 text-white text-sm font-semibold tracking-[-0.42px] leading-tight bg-transparent border-none outline-none placeholder:text-gray-4 w-full caret-accent-green" + /> +
+ {Option.isSome(field.error) && ( +
+
+ + ! + +
+ + {field.error.value} + +
+ )} +
+ ); +}; + +export const WithdrawForm = FormReact.make(withdrawFormBuilder, { + runtime: runtimeAtom, + fields: { Amount: WithdrawAmountField }, + onSubmit: ({ wallet }: { wallet: WalletConnected }, { decoded }) => + Effect.gen(function* () { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + const providerBalance = registry + .get(selectedProviderBalancesAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!providerBalance) { + return yield* Effect.dieMessage("No provider balance"); + } + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: wallet.currentAccount.address, + action: "withdraw", + args: { + amount: decoded.Amount.toString(), + }, + }); + + registry.set(actionAtom, action); + }), +}); + +const setAmountFieldAtom = WithdrawForm.setValue(WithdrawForm.fields.Amount); +const amountFieldAtom = WithdrawForm.getFieldValue(WithdrawForm.fields.Amount); + +export const useSetWithdrawAmount = () => { + const setAmount = useAtomSet(setAmountFieldAtom); + + return { + setAmount, + }; +}; + +export const useWithdrawPercentage = ( + walletAddress: WalletAccount["address"], +) => { + const amount = useAtomValue(amountFieldAtom).pipe( + Option.map(Number), + Option.filter((v) => !Number.isNaN(v)), + Option.getOrElse(() => 0), + ); + + const setAmount = useAtomSet(setAmountFieldAtom); + + const availableBalance = useAtomValue( + selectedProviderBalancesAtom(walletAddress), + ).pipe( + Result.value, + Option.map((v) => v.availableBalance), + Option.getOrElse(() => 0), + ); + + const handlePercentageChange = (newPercentage: number) => { + if (newPercentage >= 100) { + return setAmount(availableBalance.toString()); + } + + const amount = valueFromPercent({ + total: availableBalance, + percent: newPercentage, + }); + setAmount(round(amount, 6).toString()); + }; + + const percentage = clampPercent( + percentOf({ part: amount, whole: availableBalance }), + ); + + return { + percentage: Math.round(percentage), + handlePercentageChange, + }; +}; diff --git a/packages/dashboard/src/components/molecules/header/withdraw/withdraw-dialog.tsx b/packages/dashboard/src/components/molecules/header/withdraw/withdraw-dialog.tsx new file mode 100644 index 0000000..fd1f26b --- /dev/null +++ b/packages/dashboard/src/components/molecules/header/withdraw/withdraw-dialog.tsx @@ -0,0 +1,224 @@ +import { Result, useAtomValue } from "@effect-atom/atom-react"; +import hyperliquidLogo from "@yieldxyz/perps-common/assets/hyperliquid.png"; +import { providersAtom } from "@yieldxyz/perps-common/atoms"; +import { + Button, + Dialog, + PercentageSlider, + Select, + Text, +} from "@yieldxyz/perps-common/components"; +import type { + WalletAccount, + WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { formatTokenAmount, round } from "@yieldxyz/perps-common/lib"; +import type { ApiSchemas } from "@yieldxyz/perps-common/services"; +import { + useProviderBalance, + useProviders, + useWithdrawForm, + useWithdrawPercentage, + WithdrawForm, +} from "./state"; + +interface WithdrawDialogProps { + wallet: WalletConnected; + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export function WithdrawDialog({ + wallet, + open, + onOpenChange, +}: WithdrawDialogProps) { + return ( + + + + + + + Withdraw funds + + + + + + + + ); +} + +interface WithdrawDialogContentProps { + wallet: WalletConnected; +} + +function WithdrawDialogContent({ wallet }: WithdrawDialogContentProps) { + const { submit, submitResult } = useWithdrawForm(); + const { providerBalance } = useProviderBalance(wallet.currentAccount.address); + + const handleSubmit = () => { + submit({ wallet }); + }; + + const initialAmount = providerBalance + ? round(providerBalance.availableBalance / 2, 2).toString() + : "0"; + + return ( + +
+
+ {/* Provider Select */} + + + {/* Amount Input */} + + + {/* Percentage Slider */} + +
+ + {/* Withdraw Button */} + +
+
+ ); +} + +function ProviderSelect() { + const providers = useAtomValue(providersAtom).pipe( + Result.getOrElse(() => [] as ReadonlyArray), + ); + const { selectedProvider, setSelectedProvider } = useProviders(); + + const items = providers.map((provider) => ({ + value: provider.id, + label: provider.name, + provider, + })); + + return ( +
+ + Select provider + + { + if (!value) return; + const provider = providers.find((p) => p.id === value); + if (provider) setSelectedProvider(provider); + }} + > + +
+ {selectedProvider?.name + + {selectedProvider?.name ?? "Select provider"} + +
+
+ + + + {items.map((item) => ( + + } + > + {item.label} + + ))} + + + +
+
+ ); +} + +interface AmountInputProps { + walletAddress: WalletAccount["address"]; +} + +function AmountInput({ walletAddress }: AmountInputProps) { + const { providerBalance } = useProviderBalance(walletAddress); + + const availableBalance = providerBalance + ? formatTokenAmount({ + amount: providerBalance.availableBalance, + symbol: providerBalance.collateral.symbol, + }) + : null; + + return ( +
+
+ + Enter amount + + {availableBalance && ( + + Available: {availableBalance} + + )} +
+ +
+ ); +} + +interface PercentageSliderSectionProps { + walletAddress: WalletAccount["address"]; +} + +function PercentageSliderSection({ + walletAddress, +}: PercentageSliderSectionProps) { + const { percentage, handlePercentageChange } = + useWithdrawPercentage(walletAddress); + + return ( +
+ + Withdraw: {percentage}% + + +
+ ); +} diff --git a/packages/dashboard/src/components/molecules/positions/close-position-dialog.tsx b/packages/dashboard/src/components/molecules/positions/close-position-dialog.tsx new file mode 100644 index 0000000..1203096 --- /dev/null +++ b/packages/dashboard/src/components/molecules/positions/close-position-dialog.tsx @@ -0,0 +1,179 @@ +import type { DialogRootActions } from "@base-ui/react/dialog"; +import { Result } from "@effect-atom/atom-react"; +import { + Button, + Dialog, + Divider, + PercentageSlider, + Text, +} from "@yieldxyz/perps-common/components"; +import type { WalletConnected } from "@yieldxyz/perps-common/domain"; +import { formatAmount, formatTokenAmount } from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; +import { X } from "lucide-react"; +import { useRef } from "react"; +import { + useCloseCalculations, + useClosePercentage, + useSubmitClose, +} from "./state"; + +interface ClosePositionDialogProps { + position: ApiTypes.PositionDto; + wallet: WalletConnected; + children: React.ReactElement; + onClose?: () => void; +} + +export function ClosePositionDialog({ + position, + wallet, + children, + onClose, +}: ClosePositionDialogProps) { + const actionsRef = useRef({ + close: () => {}, + unmount: () => {}, + }); + + const handleClose = () => { + actionsRef.current.close(); + onClose?.(); + }; + + return ( + + + + + + + + + + + + ); +} + +interface ClosePositionDialogContentProps { + position: ApiTypes.PositionDto; + wallet: WalletConnected; + onClose: () => void; +} + +function ClosePositionDialogContent({ + position, + wallet, + onClose, +}: ClosePositionDialogContentProps) { + const { closePercentage, setClosePercentage } = useClosePercentage(); + const calculations = useCloseCalculations(position); + const { submitClose, submitResult } = useSubmitClose(); + + const isPnlPositive = position.unrealizedPnl >= 0; + + const handleSubmit = () => { + submitClose({ position, wallet }); + }; + + // Close dialog on successful submit (action will be handled by SignTransactionsDialog) + if (Result.isSuccess(submitResult)) { + onClose(); + } + + return ( +
+ {/* Header */} +
+ + Close Position + + +
+ + {/* Amount Display */} +
+ + Select amount to close + + + {formatAmount(calculations.closeValue)} + + + {formatTokenAmount({ + amount: calculations.closeSize, + symbol: "Size:", + })} + +
+ + {/* Slider */} +
+ +
+ + {/* Details Section */} +
+
+
+
+ + Margin + +
+ + {formatAmount(calculations.marginReturn)} + + + {isPnlPositive ? "+" : ""} + {formatAmount(calculations.pnlReturn)} + +
+
+ + + +
+ + You will receive + + + {formatAmount(calculations.youWillReceive)} + +
+
+
+ + {/* Submit Button */} + +
+ ); +} diff --git a/packages/dashboard/src/components/molecules/positions/index.tsx b/packages/dashboard/src/components/molecules/positions/index.tsx new file mode 100644 index 0000000..8367c07 --- /dev/null +++ b/packages/dashboard/src/components/molecules/positions/index.tsx @@ -0,0 +1,70 @@ +import { Result, useAtomValue } from "@effect-atom/atom-react"; +import { marketsAtom, walletAtom } from "@yieldxyz/perps-common/atoms"; +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@yieldxyz/perps-common/components"; +import { isWalletConnected } from "@yieldxyz/perps-common/domain"; +import { cn } from "@yieldxyz/perps-common/lib"; +import { OrdersTabWithWallet } from "./orders-tab"; +import { PositionsTabWithWallet } from "./positions-tab"; +import { TableDisconnected } from "./shared"; + +interface PositionsTableProps { + className?: string; +} + +export function PositionsTable({ className }: PositionsTableProps) { + const wallet = useAtomValue(walletAtom).pipe(Result.getOrElse(() => null)); + const walletConnected = isWalletConnected(wallet); + useAtomValue(marketsAtom); // TODO: investigate why this is needed + + return ( +
+ + {/* Tab Headers */} +
+ + + Positions + + + Open orders + + +
+ + {/* Positions Tab */} + + {walletConnected ? ( + + ) : ( + + )} + + + {/* Orders Tab */} + + {walletConnected ? ( + + ) : ( + + )} + +
+
+ ); +} diff --git a/packages/dashboard/src/components/molecules/positions/orders-tab.tsx b/packages/dashboard/src/components/molecules/positions/orders-tab.tsx new file mode 100644 index 0000000..14deb24 --- /dev/null +++ b/packages/dashboard/src/components/molecules/positions/orders-tab.tsx @@ -0,0 +1,196 @@ +import { Result, useAtomSet, useAtomValue } from "@effect-atom/atom-react"; +import { + cancelOrderAtom, + marketsAtom, + ordersAtom, +} from "@yieldxyz/perps-common/atoms"; +import { Text } from "@yieldxyz/perps-common/components"; +import type { WalletConnected } from "@yieldxyz/perps-common/domain"; +import { useOrderActions } from "@yieldxyz/perps-common/hooks"; +import { + calcNotionalUsd, + cn, + formatAmount, + formatDate, + formatSnakeCase, +} from "@yieldxyz/perps-common/lib"; +import type { ApiSchemas } from "@yieldxyz/perps-common/services"; +import { Array as _Array, Option, Record } from "effect"; +import { + OrdersTableSkeleton, + tableCellClass, + tableHeaderClass, +} from "./shared"; + +interface OrderWithMarket { + order: ApiSchemas.OrderDto; + market: ApiSchemas.MarketDto; + wallet: WalletConnected; +} + +interface OrdersTableContentProps { + orders: OrderWithMarket[]; + isLoading: boolean; + wallet: WalletConnected; +} + +export function OrdersTabWithWallet({ wallet }: { wallet: WalletConnected }) { + const ordersResult = useAtomValue(ordersAtom(wallet.currentAccount.address)); + const marketsMapResult = useAtomValue(marketsAtom); + + const isLoading = + Result.isInitial(ordersResult) || Result.isInitial(marketsMapResult); + + const marketsMap = marketsMapResult.pipe(Result.getOrElse(Record.empty)); + + const ordersWithMarket = ordersResult.pipe( + Result.map((orders) => + _Array.filterMap(orders, (o) => + Record.get(marketsMap, o.marketId).pipe( + Option.map((marketRef) => ({ + order: o, + market: marketRef.value, + wallet, + })), + ), + ), + ), + Result.getOrElse(() => []), + ); + + return ( + + ); +} + +function OrderRow({ order, market, wallet }: OrderWithMarket) { + const { cancelOrderAction } = useOrderActions(order); + const price = order.limitPrice ?? order.triggerPrice ?? 0; + const value = calcNotionalUsd({ priceUsd: price, sizeBase: order.size }); + const symbol = market.baseAsset.symbol; + const isLong = order.side === "long"; + + return ( + + + {formatSnakeCase(order.type)} + + + {formatDate(order.createdAt)} + + + + {isLong ? "Long" : "Short"} + + + + {order.size} {symbol} + + {formatAmount(price)} + + {formatAmount(market.markPrice)} + + {formatAmount(value)} + + {cancelOrderAction && ( + + )} + + + ); +} + +function CancelOrderButton({ + wallet, + marketId, + orderId, +}: { + wallet: WalletConnected; + marketId: string; + orderId: string; +}) { + const cancelOrderResult = useAtomValue(cancelOrderAtom(orderId)); + const submitCancelOrder = useAtomSet(cancelOrderAtom(orderId)); + + const handleCancelOrder = () => + submitCancelOrder({ + marketId, + walletAddress: wallet.currentAccount.address, + }); + + const isLoading = Result.isWaiting(cancelOrderResult); + + return ( + + ); +} + +function OrdersTableContent({ + orders, + isLoading, + wallet, +}: OrdersTableContentProps) { + if (isLoading) { + return ; + } + + if (orders.length === 0) { + return ( +
+ No open orders + + Place an order to see it here + +
+ ); + } + + return ( +
+ + + + + + + + + + + + + + + {orders.map(({ order, market }, idx) => ( + + ))} + +
TypeCreatedSideSizePriceMarketValueActions
+
+ ); +} diff --git a/packages/dashboard/src/components/molecules/positions/positions-tab.tsx b/packages/dashboard/src/components/molecules/positions/positions-tab.tsx new file mode 100644 index 0000000..681ef9a --- /dev/null +++ b/packages/dashboard/src/components/molecules/positions/positions-tab.tsx @@ -0,0 +1,386 @@ +import { Result, useAtomValue } from "@effect-atom/atom-react"; +import { + marketsAtom, + ordersAtom, + positionsAtom, +} from "@yieldxyz/perps-common/atoms"; +import { + LeverageDialog, + Text, + TPOrSLDialog, + type TPOrSLOption, + type TPOrSLSettings, +} from "@yieldxyz/perps-common/components"; +import type { WalletConnected } from "@yieldxyz/perps-common/domain"; +import { + usePositionActions, + useTpSlOrders, +} from "@yieldxyz/perps-common/hooks"; +import { + calcNotionalUsd, + calcPnlPercent, + cn, + formatAmount, + formatPercentage, + getMaxLeverage, + getTPOrSLConfigurationFromPosition, +} from "@yieldxyz/perps-common/lib"; +import type { ApiSchemas, ApiTypes } from "@yieldxyz/perps-common/services"; +import { Array as _Array, Option, Record } from "effect"; +import { Pencil } from "lucide-react"; +import { ClosePositionDialog } from "./close-position-dialog"; +import { + PositionsTableSkeleton, + tableCellClass, + tableHeaderClass, +} from "./shared"; +import { useEditSLTP, useUpdateLeverage } from "./state"; + +interface PositionWithMarket { + position: ApiSchemas.PositionDto; + market: ApiSchemas.MarketDto; +} + +interface PositionsTableContentProps { + positions: PositionWithMarket[]; + orders: ApiTypes.OrderDto[]; + wallet: WalletConnected; + isLoading: boolean; +} + +export function PositionsTabWithWallet({ + wallet, +}: { + wallet: WalletConnected; +}) { + const positionsResult = useAtomValue( + positionsAtom(wallet.currentAccount.address), + ); + const marketsMapResult = useAtomValue(marketsAtom); + + const ordersResult = useAtomValue(ordersAtom(wallet.currentAccount.address)); + + const isLoading = + Result.isInitial(positionsResult) || + Result.isInitial(marketsMapResult) || + Result.isInitial(ordersResult); + + const marketsMap = marketsMapResult.pipe(Result.getOrElse(Record.empty)); + + const positionsWithMarket = positionsResult.pipe( + Result.map((positions) => + _Array.filterMap(positions, (p) => + Record.get(marketsMap, p.marketId).pipe( + Option.map((marketRef) => ({ + position: p, + market: marketRef.value, + })), + ), + ), + ), + Result.getOrElse(() => []), + ); + + const orders = ordersResult.pipe( + Result.map((o) => [...o]), + Result.getOrElse(() => [] as ApiTypes.OrderDto[]), + ); + + return ( + + ); +} + +function PositionsTableContent({ + positions, + orders, + wallet, + isLoading, +}: PositionsTableContentProps) { + if (isLoading) { + return ; + } + + if (positions.length === 0) { + return ( +
+ No open positions + + Start trading to see your positions here + +
+ ); + } + + return ( +
+ + + + + + + + + + + + + + + + + + {positions.map(({ position, market }) => { + const marketOrders = orders.filter( + (o) => o.marketId === position.marketId, + ); + + return ( + + ); + })} + +
CoinSize + Position value + Entry PriceMark PricePNL (ROE %)Liq. PriceMarginCloseTake Profit + Stop Loss +
+
+ ); +} + +interface PositionRowProps { + position: ApiSchemas.PositionDto; + market: ApiSchemas.MarketDto; + orders: ApiTypes.OrderDto[]; + wallet: WalletConnected; +} + +function PositionRow({ position, market, orders, wallet }: PositionRowProps) { + const { updateLeverage } = useUpdateLeverage(); + const { editTP, editSL } = useEditSLTP(); + + const positionActions = usePositionActions(position); + const tpSlOrders = useTpSlOrders(orders); + + const symbol = market.baseAsset.symbol; + const value = calcNotionalUsd({ + priceUsd: position.markPrice, + sizeBase: position.size, + }); + const pnlPercent = calcPnlPercent({ + pnlUsd: position.unrealizedPnl, + marginUsd: position.margin, + }); + const isPnlPositive = position.unrealizedPnl >= 0; + const isLong = position.side === "long"; + + const initialAutoCloseSettings: TPOrSLSettings = { + takeProfit: getTPOrSLConfigurationFromPosition({ + amount: tpSlOrders.takeProfit?.triggerPrice ?? undefined, + entryPrice: position.entryPrice, + tpOrSl: "takeProfit", + side: position.side, + }), + stopLoss: getTPOrSLConfigurationFromPosition({ + amount: tpSlOrders.stopLoss?.triggerPrice ?? undefined, + entryPrice: position.entryPrice, + tpOrSl: "stopLoss", + side: position.side, + }), + }; + + const handleAutoCloseSubmit = ( + settings: TPOrSLSettings, + actionType: TPOrSLOption, + ) => { + if (actionType === "takeProfit") { + editTP({ position, wallet, tpOrSLSettings: settings }); + } else { + editSL({ position, wallet, tpOrSLSettings: settings }); + } + }; + + const handleLeverageChange = (newLeverage: number) => { + updateLeverage({ + position, + wallet, + newLeverage, + }); + }; + + const tpValue = tpSlOrders.takeProfit?.triggerPrice; + const slValue = tpSlOrders.stopLoss?.triggerPrice; + + return ( + + {/* Coin column with clickable leverage */} + + + {symbol}{" "} + {positionActions.updateLeverage ? ( + + + + ) : ( + {position.leverage}x + )} + + + + {/* Size */} + + + {position.size} {symbol} + + + + {/* Position value */} + {formatAmount(value)} + + {/* Entry Price */} + + {formatAmount(position.entryPrice)} + + + {/* Mark Price */} + + {formatAmount(position.markPrice)} + + + {/* PNL */} + + + {isPnlPositive ? "+" : ""} + {formatAmount(position.unrealizedPnl)} ({formatPercentage(pnlPercent)} + ) + + + + {/* Liq. Price */} + + {formatAmount(position.liquidationPrice)} + + + {/* Margin */} + + + {formatAmount(position.margin)} ( + {position.marginMode === "isolated" ? "Isolated" : "Cross"}) + + + + {/* Close column */} + + + + + + + {/* TP column */} + + {positionActions.takeProfit ? ( + + handleAutoCloseSubmit(settings, "takeProfit") + } + entryPrice={position.entryPrice} + currentPrice={position.markPrice} + liquidationPrice={position.liquidationPrice} + side={position.side} + mode="takeProfit" + > + + + ) : ( + + {tpValue ? formatAmount(tpValue) : "--"} + + )} + + + {/* SL column */} + + {positionActions.stopLoss ? ( + + handleAutoCloseSubmit(settings, "stopLoss") + } + entryPrice={position.entryPrice} + currentPrice={position.markPrice} + liquidationPrice={position.liquidationPrice} + side={position.side} + mode="stopLoss" + > + + + ) : ( + + {slValue ? formatAmount(slValue) : "--"} + + )} + + + ); +} diff --git a/packages/dashboard/src/components/molecules/positions/shared.tsx b/packages/dashboard/src/components/molecules/positions/shared.tsx new file mode 100644 index 0000000..c79b193 --- /dev/null +++ b/packages/dashboard/src/components/molecules/positions/shared.tsx @@ -0,0 +1,136 @@ +import { Skeleton, Text } from "@yieldxyz/perps-common/components"; +import { cn } from "@yieldxyz/perps-common/lib"; + +export const tableHeaderClass = + "text-xs text-[#707070] font-normal tracking-tight text-left"; + +export const tableCellClass = "text-xs text-white font-normal tracking-tight"; + +export function TableDisconnected({ + message = "Connect your wallet to see your positions", +}: { + message?: string; +}) { + return ( +
+ Wallet not connected + {message} +
+ ); +} + +export function OrdersTableSkeleton() { + return ( +
+ + + + + + + + + + + + + + + {[1, 2, 3].map((i) => ( + + + + + + + + + + + ))} + +
TypeCreatedSideSizePriceMarketValueActions
+ + + + + + + + + + + + + + + +
+
+ ); +} + +export function PositionsTableSkeleton() { + return ( +
+ + + + + + + + + + + + + + + + + + {[1, 2, 3].map((i) => ( + + + + + + + + + + + + + + ))} + +
CoinSize + Position value + Entry PriceMark PricePNL (ROE %)Liq. PriceMarginCloseTPSL
+ + + + + + + + + + + + + + + + + + + + + +
+
+ ); +} diff --git a/packages/dashboard/src/components/molecules/positions/state.ts b/packages/dashboard/src/components/molecules/positions/state.ts new file mode 100644 index 0000000..719d57b --- /dev/null +++ b/packages/dashboard/src/components/molecules/positions/state.ts @@ -0,0 +1,178 @@ +import { + Atom, + Registry, + Result, + useAtomSet, + useAtomValue, +} from "@effect-atom/atom-react"; +import { + actionAtom, + selectedProviderAtom, + updateLeverageAtom, +} from "@yieldxyz/perps-common/atoms"; +import type { + TPOrSLOption, + TPOrSLSettings, +} from "@yieldxyz/perps-common/components"; +import type { WalletConnected } from "@yieldxyz/perps-common/domain"; +import { getCloseCalculations } from "@yieldxyz/perps-common/lib"; +import { + ApiClientService, + ApiSchemas, + type ApiTypes, + runtimeAtom, +} from "@yieldxyz/perps-common/services"; +import { Number as _Number, Effect } from "effect"; + +export { ApiSchemas }; + +export const SLIDER_STOPS = [0, 25, 50, 75, 100]; + +const closePercentageAtom = Atom.writable( + () => 25, + (ctx, value) => + ctx.setSelf(_Number.clamp({ minimum: 0, maximum: 100 })(value)), +); + +export const useClosePercentage = () => { + const closePercentage = useAtomValue(closePercentageAtom); + const setClosePercentage = useAtomSet(closePercentageAtom); + + return { + closePercentage, + setClosePercentage, + }; +}; + +export const useCloseCalculations = (position: ApiTypes.PositionDto) => { + const { closePercentage } = useClosePercentage(); + + return getCloseCalculations(position, closePercentage); +}; + +const submitCloseAtom = runtimeAtom.fn( + Effect.fn(function* (args: { + position: ApiTypes.PositionDto; + wallet: WalletConnected; + }) { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + const closePercentage = registry.get(closePercentageAtom); + const closeCalculations = + closePercentage === 100 + ? null + : getCloseCalculations(args.position, closePercentage); + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: args.wallet.currentAccount.address, + action: "close", + args: { + marketId: args.position.marketId, + side: args.position.side, + ...(closeCalculations && { + size: closeCalculations.closeSizeInMarketPrice, + }), + }, + }); + + registry.set(actionAtom, action); + }), +); + +export const useSubmitClose = () => { + const submitResult = useAtomValue(submitCloseAtom); + const submitClose = useAtomSet(submitCloseAtom); + + return { + submitResult, + submitClose, + }; +}; + +// Leverage hooks +export const useUpdateLeverage = () => { + const updateLeverageResult = useAtomValue(updateLeverageAtom); + const updateLeverage = useAtomSet(updateLeverageAtom); + + return { + updateLeverageResult, + updateLeverage, + }; +}; + +// TP/SL hooks +const editSLTPAtom = Atom.family((actionType: TPOrSLOption) => + runtimeAtom.fn( + Effect.fn(function* ({ + position, + wallet, + tpOrSLSettings, + }: { + position: ApiTypes.PositionDto; + wallet: WalletConnected; + tpOrSLSettings: TPOrSLSettings; + }) { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + const newStopLossPrice: ApiTypes.ArgumentsDto["stopLossPrice"] = + tpOrSLSettings.stopLoss.triggerPrice && + tpOrSLSettings.stopLoss.option !== null + ? tpOrSLSettings.stopLoss.triggerPrice + : undefined; + + const newTakeProfitPrice: ApiTypes.ArgumentsDto["takeProfitPrice"] = + tpOrSLSettings.takeProfit.triggerPrice && + tpOrSLSettings.takeProfit.option !== null + ? tpOrSLSettings.takeProfit.triggerPrice + : undefined; + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: wallet.currentAccount.address, + action: actionType, + args: { + marketId: position.marketId, + ...(actionType === "stopLoss" + ? { stopLossPrice: newStopLossPrice } + : { takeProfitPrice: newTakeProfitPrice }), + }, + }); + + registry.set(actionAtom, action); + }), + ), +); + +export const useEditSLTP = () => { + const editTPResult = useAtomValue(editSLTPAtom("takeProfit")); + const editTP = useAtomSet(editSLTPAtom("takeProfit")); + + const editSLResult = useAtomValue(editSLTPAtom("stopLoss")); + const editSL = useAtomSet(editSLTPAtom("stopLoss")); + + return { + editTPResult, + editTP, + editSLResult, + editSL, + }; +}; diff --git a/packages/dashboard/src/main.css b/packages/dashboard/src/main.css new file mode 100644 index 0000000..adef89f --- /dev/null +++ b/packages/dashboard/src/main.css @@ -0,0 +1,11 @@ +html { + background: var(--surface-2); +} + +#app { + background: var(--surface-2); +} + +#widget-container { + border-radius: 12px; +} diff --git a/src/main.tsx b/packages/dashboard/src/main.tsx similarity index 76% rename from src/main.tsx rename to packages/dashboard/src/main.tsx index fcdc99a..a66448c 100644 --- a/src/main.tsx +++ b/packages/dashboard/src/main.tsx @@ -1,9 +1,9 @@ -import ReactDOM from "react-dom/client"; import "./main.css"; -import App from "@/app"; +import ReactDOM from "react-dom/client"; +import { Dashboard } from "./app"; const rootElement = document.getElementById("app"); if (rootElement && !rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement); - root.render(); + root.render(); } diff --git a/packages/dashboard/src/routeTree.gen.ts b/packages/dashboard/src/routeTree.gen.ts new file mode 100644 index 0000000..d204c26 --- /dev/null +++ b/packages/dashboard/src/routeTree.gen.ts @@ -0,0 +1,59 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' + fileRoutesByTo: FileRoutesByTo + to: '/' + id: '__root__' | '/' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/packages/dashboard/src/routes/__root.tsx b/packages/dashboard/src/routes/__root.tsx new file mode 100644 index 0000000..840d417 --- /dev/null +++ b/packages/dashboard/src/routes/__root.tsx @@ -0,0 +1,17 @@ +import { createRootRoute, Outlet } from "@tanstack/react-router"; +import { Header } from "../components/molecules/header"; + +function RootLayout() { + return ( + <> +
+
+ +
+ + ); +} + +export const Route = createRootRoute({ + component: RootLayout, +}); diff --git a/packages/dashboard/src/routes/index.tsx b/packages/dashboard/src/routes/index.tsx new file mode 100644 index 0000000..a2cd5b0 --- /dev/null +++ b/packages/dashboard/src/routes/index.tsx @@ -0,0 +1,6 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { TradePage } from "../components/modules/trade"; + +export const Route = createFileRoute("/")({ + component: TradePage, +}); diff --git a/packages/dashboard/src/styles.css b/packages/dashboard/src/styles.css new file mode 100644 index 0000000..996bc2a --- /dev/null +++ b/packages/dashboard/src/styles.css @@ -0,0 +1,17 @@ +@import "@yieldxyz/perps-common/styles"; +@source "../../common/src"; + +:root { + --background: #202020; + --content-background: #111; +} + +.dark { + --background: #202020; + --content-background: #111; +} + +@theme inline { + --color-background: var(--background); + --color-content-background: var(--content-background); +} diff --git a/packages/dashboard/tsconfig.json b/packages/dashboard/tsconfig.json new file mode 100644 index 0000000..6578f19 --- /dev/null +++ b/packages/dashboard/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "noEmit": false, + "outDir": "./dist", + "rootDir": "." + }, + "references": [{ "path": "../common" }], + "include": ["src"] +} diff --git a/packages/dashboard/vite.config.ts b/packages/dashboard/vite.config.ts new file mode 100644 index 0000000..c02ea85 --- /dev/null +++ b/packages/dashboard/vite.config.ts @@ -0,0 +1,4 @@ +import { commonViteConfig } from "@yieldxyz/perps-common/vite.config"; +import { defineConfig } from "vite"; + +export default defineConfig(commonViteConfig); diff --git a/packages/widget/.env.example b/packages/widget/.env.example new file mode 100644 index 0000000..6dc0341 --- /dev/null +++ b/packages/widget/.env.example @@ -0,0 +1,12 @@ +# Perps API Configuration +VITE_PERPS_BASE_URL= +VITE_PERPS_API_KEY= + +# Reown (WalletConnect) - Get your project ID at https://cloud.reown.com +VITE_REOWN_PROJECT_ID= + +# Moralis API - Get your API key at https://moralis.io +VITE_MORALIS_API_KEY= + +# Optional: Perps OpenAPI docs URL (for code generation) +# VITE_PERPS_DOCS_URL= diff --git a/packages/widget/index.html b/packages/widget/index.html new file mode 100644 index 0000000..b92d082 --- /dev/null +++ b/packages/widget/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + Yield.xyz - Perps Widget + + +
+ + + diff --git a/packages/widget/package.json b/packages/widget/package.json new file mode 100644 index 0000000..5be4692 --- /dev/null +++ b/packages/widget/package.json @@ -0,0 +1,66 @@ +{ + "name": "@yieldxyz/perps-widget", + "type": "module", + "scripts": { + "dev": "vite --port 3000", + "build": "vite build && tsc -b", + "preview": "vite preview", + "lint": "biome check . && tsc -b", + "format": "biome format --write .", + "generate-routes": "tsr generate" + }, + "dependencies": { + "@yieldxyz/perps-common": "workspace:*", + "@base-ui/react": "catalog:", + "@effect-atom/atom-react": "catalog:", + "@effect/experimental": "^0.58.0", + "@effect/platform": "catalog:", + "@effect/platform-node": "catalog:", + "@ledgerhq/wallet-api-client": "catalog:", + "@lucas-barake/effect-form-react": "catalog:", + "@reown/appkit": "catalog:", + "@reown/appkit-adapter-wagmi": "catalog:", + "@stakekit/common": "catalog:", + "@tailwindcss/vite": "catalog:", + "@tanstack/react-devtools": "catalog:", + "@tanstack/react-query": "catalog:", + "@tanstack/react-router": "catalog:", + "@tanstack/react-router-devtools": "catalog:", + "@tanstack/react-virtual": "catalog:", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "effect": "catalog:", + "lucide-react": "catalog:", + "react": "catalog:", + "react-dom": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", + "tailwindcss": "catalog:", + "tw-animate-css": "catalog:", + "viem": "catalog:", + "wagmi": "catalog:" + }, + "devDependencies": { + "@tanstack/devtools-vite": "catalog:", + "@tanstack/router-cli": "catalog:", + "@testing-library/dom": "catalog:", + "@testing-library/react": "catalog:", + "@tim-smart/openapi-gen": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "@vite-pwa/assets-generator": "catalog:", + "@vitejs/plugin-react": "catalog:", + "@vitest/browser-playwright": "catalog:", + "babel-plugin-react-compiler": "catalog:", + "jsdom": "catalog:", + "openapi-filter": "catalog:", + "tsx": "catalog:", + "typescript": "catalog:", + "vite": "catalog:", + "vite-plugin-node-polyfills": "catalog:", + "vitest": "catalog:", + "vitest-browser-react": "catalog:", + "@tanstack/router-plugin": "catalog:" + } +} diff --git a/packages/widget/public/apple-touch-icon-180x180.png b/packages/widget/public/apple-touch-icon-180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..a23de91c42842f6668c72598ab99b98096a9e3b3 GIT binary patch literal 3095 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 literal 0 HcmV?d00001 diff --git a/packages/widget/public/favicon.ico b/packages/widget/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e2950e3e36b0bd61fcd6a26eb1939410bd9f5b7b GIT binary patch literal 1609 zcmV-P2DbSC0096205C8B0096905b*v02TlM0EtjeM-2)Z3IG5A4M|8uQUCw|FaQ7m zFbDVnC;GcnHDWdz$askGV2A)81WnWrjb_T;{&^`_At>-|w7@g41kQjOI{S z8vr*(Kx2In+!z7P=naLn0dV&^fgK_0afq|2hm?1V9UkzLmUX_=wj$FIhJyONq zXE6d&@J;E*y#TUC)3BTYtl5}(=Hi=qtt|y*eLq#y8vZ)dK>aPh5`X>AJEFb0j%spK zPRb!+5_s_iiiuNK%nA4;dBH1xubuInu*4(C@QjBqsomWvWzGz`5bs4di^GYUOXKnJ z4np*_DFVopaxV1srkMti`Y~yi7*`YAd7Q(w6I<0^O5Z(V1X%W=ZSMzMJDuz%le227 zFnyvUfy4xb4G%&HoX2&9o2`mKXznBjdP?Ou6gY=#IZ@#))X`&WjEqdmJ`p)=2#ffh zfd*K}XOnLvrLNRc!KRO>LD$%uuzEdBR@f2RI(5$ezoJUbRyfwxvI3Zk(VG zNu=7CRN*sCQoo!6mQ6rftm4uSUg8@P{RM1D$UI~OLXeEKzd=C%-zDY5AT)2yB>MgX$-rLl<^0qn5QJe=m%=_V}$gpL${FL_cOP@DdiaI;KLH;xwz zJ*P^;-2>h;2vI(-v! zF=GcClZ*G;y3^OuRiYt-gaF=q(xUY$v2i7=*p3Px?Cn6u_D;5 z<>+5F0coa=msKur{k*UjHi?D|mdLG`JnCEN?JwTYGG)my4A;fQDDNXIM$*c{9X z6m|+vhc&`spm%5-O&%sSX_lpJbGJ{4x#?cNkN%&dDVp9pdqI0Xr&2 zLAVbHKt9Mmq=O>G7YD2mz?us%CzpV9_eEB#W0#5Z?XxQTL9`F7c%l7{WA*gM!ILR0 z>mvo8`@aMjcpUKs;{5&yaTv$24)E}P5I7N^$-{AeJJ+$x+*0ApK>$uO96W2@vbXLM zuqzMt@oUUgpq#k~Fvk&R+}LFV*|4t==zTJh_7D+uG4QNtEfET12%uv`&yS3=*%kvm zz+)!1G zHp!Wr03*S^L|79%H})?kjrBueZ2;UD0gd%RaAO2Cqc{Em6h$MLo-Fi500000NkvXX Hu0mjfIP&6t literal 0 HcmV?d00001 diff --git a/packages/widget/public/logo-192x192.png b/packages/widget/public/logo-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..b49da70c9942d5d7a98ddb0e8f98436b8d35933c GIT binary patch literal 6071 zcmcIoS2P>|vtD(H-a?chYND)OW08muq6?z;-bIMEM2Sw6NOTfbm&7X3ONhRrM_(*a zSGTs^{P*?V`*`P^Z)VP%IrH$HnR)mU4fLMU++x24003yTwKR1)A4fpK9ba?@(82 z^l`Iv)I?M-)9Y>Y{gTqal3H8sb?BY|9m`Zs(|$|CX>Z*@|M}nF;|HEM_z=R)awF>9 zB?~$A9ST}z@>{GxYEB9|HXtn-fQW~Zfj}VOf7fvUnP`ZBAZmI+N;=GV?L8nHdbnDR zR^n=)Mwo&NM9EJ;Bf6d5xp5foRezSJ5g}lYpD@xBaA;(C@mXihuwMqM1-kL69EKPdXny!{Y-&z!It3p0O<8?nFI-#yNMNVtS6(b1=funXgh0l+iuQ|5nyV2#x?IFWO_$w zIarL3>2~k)2~=nPFK9xDr#pWB!{Iv9VN%NcT z*xwN^tFwzo3XMjeH+8wM=R6u%)gQ4Ecu=CDe^BJ;rOaKI?p;W6ja|CiFgCcJ1S*5w}ylzv(kf&V_k?tef83p4Tr0GQ0QHPlT) z{v2Alnlro3-_8Bju0_MeAR1dHEGH_NC^qMolE`i>s}h>kp}l%J?)A|+*!&Y&Mj10p zst%8wL=th5Fpn041{0Ct0Hf^XdxyQS3F&qbxBBBBU8-Xt5rH+0Ke?|W{sb1CjZr_* z&frmxA*E#G*ZN-))UKL%6u-6zMJ;!aC%Rmn^W*E5HY>{YH4%*is-o7}^#BYwIPEQP zl0*42$IQl}74??Rh8r9L$rmmk_6itPCSxqVS`C||Q+ac~JGq9(o=vI(iqu54`rAD8f)MUwy{ zuU4pzeXc$!N+TA&UVgWpCn0&BLrY=2eo=q<4i4W+?7LC(an{oX`&^4;2{9qnuyL-d z)R#$WNb6|9thcKvGScfgRin~P9uNqahvBUtw|w|f-|zr(jVO@kDC{mu*#Knu)nV4w zk<4y3$+7fkA_fiET~fiXo^^~T4hM#-G!&r^TXyiMuCS2f;m69aBjc_a8xiW^ZN%WX zzLIg+f^NUlpMHRxG1p2{=dyQ;12 z==lA6G5x9w@Q%CL!9`?rw+DIKO=g!ke#%4UM_zbYX@?u3m)(R&%0Nqgi}$MTd8(}J zsx)(F9pLmT)n!fZML+#_>6dnO&cEnnvE)R%6SOWn_dO?Wp%j*)SyM6lk{$03^I&|odkb!-L9_T%K>VVltOE)y zwI|?lKn7HMX(>hP@qOrPf5pN@%&32{e8B`DN>;M{<#SJW*vnMVI#YIuraV1^BoDD^ zc54B&0VKTOsHv`wOJouk__t_pDH&b&;!~nq zlq+j?Tm9u4f7@$onRJ&w9@>pCr-@9x>+pk!FD=8>Z+6`Xl;U@A=%b}g(^hUI34fyY z?S0-xTYfI-lM9ZU1!H}ci-jgXx-21=A1}vI+GzunuVn9cBggDp4;Z>S#R1_Gr(SVM zPyEJNxV#VN-^M)Z3q8_Txm=h+88(torCb;Cfr$}Fb~1+$S>(Ki8cMg_#`f1%_L5FT zmG_WxTFc5u7UG#o6_>##?*7M`VxgW}yS!Ooc%i-98KS2lsHts4${@_$CLy=|!{%(| zRlA~q?6CcCY&!T8y=(tMQrne0>I^RvkQ4ox0=oxTY)bX9}I zozR$!SoQ_cyXcUUOmt-xg&-2OCvNZcaJk0as-E=Z467$=rjlVcmr>wtC`$w&=&2td zNMIv@mm$%DBF{owqU)BWkfdizxI*t>mASD!2tT9QiMqxrJe;RWA`-=9`u!Lb9FrRV z>oXes`mA=_A|B?hIO(rcB>n)h*60;2Vlu#V4J7*An#6#Q(2!Y*LxsNIoPZVB!`P|0 z;urEO?pPgCI6H)~jPLi{3$m{d5$p)R3?^5*miR?41yN9|a|XgzP#a?dvmGQRyYv&F zpaK+$O%0l*p~}2FqDXXzM}>mDHm@h^km4Mrr92^(3pcd4+7KCBiR#cI71la&4Y#as zG_dTMNXZqd+Q{Q@cJ>t?p+QoFp3!|9@ESUyuWJ=qzgx3=@ z`0tS~$*b7yq%?g8+3pmg)+B?E!U5)b-#)xrd!1t{Ie_)(T7}Nm>N^?08R>AbleV-+ zTVvbBjYBW=g4anT71Z>S2t4!EdM}c1lb<Eetjxqgc_^`PMfd$9E1Ia+>zGjBfs!prPT2+}1EqQ^47=iZf! zPrt2r>xc*(?q(~D|2#kCK+8l%k0<(Y>LU~zu}9sRRh~7L=+ZQ2=wxM8pkzNsYFSEV zcfviW^d$>H$1oxBd9zn5CSC^Q8yqF5++A#D6Ok)s*<4`5CZ)5R^~?yX zIA)W^`$9-n8#pu)IX)UTp+Fs+(lcdqIidLHQN&_^#w}dt&{E&&j%7;{M9EF%j#Yp* zVa(S;pMaO^>wU?Ee! zq4Q8q^$%RfVzuhi{GoPE=3~+}rJok94I~Gvxdseda9cH^@XP8KgBZJga?O7{BJ+Xq zTtRL0eLJZqSc{5+J;zOOpyK@q)%udL5ay#~6fc~FG`!l2%B{%-&j==i;8b+n4Ma0>I99rNEd+Mh((MQ*C zGm!NUZt#{WT?=JK-6jp)vb^u&H)q^U(t11@@o7r}BC38ubr5Pe?F6qu$|v!Me=Cxf zQ`NB$FM>#>q=xnQeBX$^x#3do0ylz1*JH~K_9fg^653wi3^Tl)PzLcEL(?+lE)90q zZj#~5SNhBaE;oWFThW+#VAnMwc~(9>tgw;gAbZK5O{As<(^b`>Kt9xrQz+8zk5LS>C;fGQ$-Xu$93h{OKceFh#6I{?%*3-5 zIoc8_urZygItKDuo^G*Q;6cyUnE9?1<+qD?{kS5I-lLOoDR6zYXtu@)w%&T*AYIJ- zSg03<58ZE0zC2#(OHgI-4;WvuccEJj3=9Z%M#hTyd;;m-5{CL0gu&d&Zn-HC5nuDY zNYg%Pf8`lmP1IlM>NEy(w{W*F{_bh!;^K1i=cDn<=39Yn^rQxXkK)}~7#ay0)eLk4 zkf@(rAnM|5|I?!2pA@H7j~B|frKhK-v$C?JCn7?wF<}y3sl}On?8*l`^mb@%98v3xY&zw| z2aGHZN`r?+z}FPKhhSS_cfLI>3V%{Wh4!^pdzNd?KG4j>Y`BmGpGE1+$l)U zxsX^&s_uY^iD`3_sfO=QSKF*Dl(})(Ps?)lIY6;qy76$9>R^k`CWGo}OrZ0L8UY-p zaoDEJLEtkHxw-lHg-ucKGD|;CQpMe%tSF7ht&tq_3@jxH>t!%g8*_k&I!KKKh zI$=P&;#Rj;=*W*1Z4NBRD$m|?1!^~ zqnj2j^zV$DLUA|7mCHjf8JTZrul6*GBAZyg+E3Gw{h^xEN$(`ac4POpR%b++RTq>V?E1CO>#fPV~M6<=ZPO0f@V^c*3KexDTAE{mssoavPkTlbu@u>GaK-T4C zXlnCKN%Q5Ex^ZrZt~t-TIs1!wv-XPX0zy}li@ra2QuPavaJ~ggtan`#R4*$*VnT?0 ziz2*p^cRBY(hUiXtN~C8uD_Ny``+MgmKoNeT~RW(exwg}o_`MZVewSl4^un76K<*# z?xiz(Y^Hofno6ud6~0fhEhjb3yE~CBqUp$K(JMhTcdTHyU7WcfHMMmo-KhOvUp~5b30$`D8pP z=@z%N%UJqGu>zISG;B3v2aD)?b*%g0js)bI6o79J!6#0cx;~B>%hA-Mxt76=N0KJr z7JIX16M|=s`WoB02^5*XO9aeg>KRc5gKritBImRc)CV!edTdO0SAD0#();g;k>j@< zC`I4zXfE()0QyK*bB32x!9Tu4%;{elvv3L?dn(&O>LSwjH3s~^ig{O@WHQLk8}n7Q zdJb&iTy*gV@B_T8AyK9nn4)T&epz;Q2ocu17-2P7WVC)2TV-;DtmV@{jCdvgIy)97013fY*dnp#oBWbzmC)Y) zR=)Zc>?WfYIXzGfpiXQu56h2?2D~xspadXahpo`>*t~9;h9o!65}8L=*G%LR5z0YH z^Z1@9wLIS5mI+%D?`5SfQVULc>siy_K#~oR5mC=*znVk1sohrRtH6O#Zp%-smE-`3ICohrRktWhRV-GW!nv(m+`VK*=t&j{&M4PFm+a3{9!EIS^+OiIV8Xb(8BgpA@fz zG(y4oYgCT6>}ZIA{6c%Is;mZbD|#|!-@qM-*sGTOV|19Wx99`mcz^l|HDV1oZA!`3 zx#!Jw|Mw`gZY_?a2~3biL7!I>{&oz;s|e7m@jz;cEA3d`%0WfN*8B1&d(6#vjqnqM zyVo7Ycy5c(RLrRUy6(yYfU%v~Vl{*1phCyC;BP#UoH$j&YKnWOW!`*HV+UB7Tj&Q5 zARw=nxLvn`B&+#rX7tn}6X7W`$qm0tPY&;K?B#CZ7c4Xo|%8z93Zw*<**l~SSU zpAa}FfjuCR`{7T=#c#2}xKB<%oDS~EL)GYBuP0k8zm3eh_t4k#r*s+cfjdf*d(!np zQLd7cQR{YL&uXvcKEiyE>WTH;f$D_dyN`Ni=e~=Lm+(%)6baDl=5j|7EEAjFGhD zK2B4tmsh_giTZL~{86~lChgE9i)xcCAWZu4ckee$vFY!Y5`b_12C3=|eTbsw&HY$C zgklK9pdc}!tf>XLQFnn4pCX>IqF&R#!l2+eWYQf`##uXBjnyK?)110*GF#}PN=pR5 zh}QJU6Ao+lye`_CmI7`rJXB3`uBCI#>E)LOQ4vd`6V=~`h6w$;9*PrR>;|ME-&j!m z@w@?OzMsXM&_Cj+F5FR*`_I?f-eZ}e?% nFIlwzUnIN>V@(K@k>E5Cx>AVd;{T&ZWDX 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 literal 0 HcmV?d00001 diff --git a/packages/widget/public/maskable-icon-512x512.png b/packages/widget/public/maskable-icon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..a5a42f54a5572da4159fbdbf8211514825040430 GIT binary patch literal 12755 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 JO3gm%{{Tdfnu`Dc literal 0 HcmV?d00001 diff --git a/packages/widget/public/yield_xyz.png b/packages/widget/public/yield_xyz.png new file mode 100644 index 0000000000000000000000000000000000000000..1bbfc87ce3a60434716a55f311414481cd46c914 GIT binary patch literal 235471 zcmd?PWmB9{vo$>U;Db91?ryUm^!1Qr6YQv!&RktB}+wuzSUer9jwaVPXVl}ooh0DZ6qOHZAUkuhG ztOT=bu?3(=gX^)W%QU_uee-|uO80oEGoLJfQ0hmt7yP0f{FA6{>gNto=6Cx%7l*gM z!q;yW2Qxaz-LD%Dot8pxcXOF~Ea~4*l{>hJ;1aRiD1Np#rmmM>m>E8_F787$t=qS) zf8`b6;!jCd!wn$b!ldVo5Vu}-2fzkEQ-EQ`OEe%F_@$uD+jcgTcF5{=VdQKI{K@>u z#ertm^8o4)MymVj#;tExU48xD$8*(3>rQxhN{R-*y4rtm52x7q7KjjYquKS%u**;A zWgFeU`QNt!|JsbfxX{MOT5x36OrwW4OiQGh$3pdW2mJE#)k#L~pey&Gw)t#MFG z7upn+bZlCTSiY2Lo??ef^S#%$5wzK1Hf?@cZa-dyt7GQ)Q?mrs7|3h{vjp3i=eZL( z�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�K;+083_H%Cz^;m9 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

082{Bdsk;xvBuv_HC=$% z`C){ic?PuR-vun-Yfme;D=%fXcSX7uLqw{3{z z3WOnKW~Fut*0?L_zd(?c;`@LIwr| zbt0W<1;Ye7Hi^xIYr?}r4WR)m69psTbe$+1bj*UmBDg)_D>BHXnx`E|1KLu@2bTtoA?-yWxd8&f(Df&C%LL`{GYo|Ui?}jb zLLf{B>&N6((OL@IYut@GuV;SY_T1AYuZRi{FsMgX+UNS`>}_ESYs;EfErW2*vY$OTgmaH){ar^a^B>7_&!>7idqE7>+ zF*v6CGoYji@?ogwN?NteFZ%4q4YmU;K{h5x2O<2RTH_@?^4|Ctm`!9z_X! zDOLJ9RRjkM1|39v1Ap!FB;(kZRqcJ#H@o4;qH+)(S1w zLtG@6LTon1Tofid!>dW&d8r}+T2IWT$!)#R1@vULd^I}%ed!~d7y;Bjmt6MI#jbXr zrN6!VK5F-5#MP-8;H&kQbufFLX|`>%?X0d=Nn)z!r>s{gqYNbA|(k}#pmW}mCrEswDZSeG8+7wy}hB+8v46k5h+0$Od{gL;dx|R zbGB)bH~b#%f{R%^C~>KPDkaZBRF=$9=@gZFkd^YxT__0V{^uf~&%|hWI2#nY>CuBYL3uNPzIQT1HjnTeyO=ZCHd5D z-9X7#Y`n-X@JJdYtKo`JAg%U1jr88GByu~voqnZ^@AY?ov**@ZXFOb)=mcAW8YgMFN6v=)-~TYCoY^$<~xnJVTegEA`PrwJim(ifk8E80JqKZ0&Wy@KV86I)tc z&@BOKPJ&;)brl8@n=3-(Y=O2g2;lhoymJcbN+JkASO96E1PqQ62mh6duakIyM*=q~ zk(7X)Pd0H$mK9nyS805=LUekTZ1ZNC%2y2rB`*9G_l@9QI+1s4Hx8RdmE%RZ^4{Ek zc1EnIH^D~yZMSD7`JMk=w5*0sUw$>AsKngkOIY*0VV*tXKRv_m{cxlY`T6-Vky(vr z6jV<;%l+F_w@oL>F+Qn;#j5%HYOB0$7esF>z4d)}U;PnI>8tLNn*~b1>fYY$Y2DJ? zi)*hHG9%Na^t2@5!U8a8!GsHb)IdNMxQEC(hc--3n@18U)adjW$_fKXeGJf}-ebpk z!9|2lqz#k=Yr^_rFy!%pORZe8$iqdwthTbAiRHUV$yFYEpac=tx)GJ9xVP}^Bm=AG z5p09Iour;9OrtA~2`89tA#~++%nxmqLpF7R7c2j_paO1n01NH0;q|f4>T4?ZQQsL3 z+uNqIhx5*Ro(Z)-}ld{rA3R+!O;H^At7!jViOze#*l2r*t z*` z=Zh#_^{Wf?Z(RUG_nB8W^Tj_nokj*vJM00~y40VRiCi8wO|ixIKlhGc#a>JHm6;z$ z49)DF-#y|R;01k#myx*yeE#luq$$c1%IXKDjF7U@Qes`9QDXI@Vbra#B%V-$Ap;9Q+Y^-Rl40ZLV3JyAbPSqc%rBgSh;e+LA-7Iy-=(eHEq3@{M?wmp z>+Ez7y5bkV9VkESt$KJ7XrnB4lc62S!R>OCVm7hV&Xj$0Zr^4EB@D?lGQZJvKI(=^ zsF0@gp%4&*SWYWgI5Z$9Kvq@gS;s2#dS*!&n#xqlG(P|zn@JC%fNy)OL0pI^rC{{&H+Tr z>?vE&nG!XU+|@q}*Yqm?oEQD%=H}L?=KH$fzxB>3PgBw}+VIFW6YBc7>pQ+`6SnMo z${ATPvU7A$=eg}S>fJPGc+BEHhn~cnkQ9LamV~kcd}egx0cJovQ)H2HswGe)K^tgj zHGkuJNuTLpb-!rZaJs_dkvrOFJi-AixF9#~A#6f05a~s~mL4=}Hg8bFqIth?`=mGh zkvkKIg9gcjx)cAjiLdT@+B;JG9x&H8D&dPHgq%j9kG}!MSIuW6_pn`R=0C2Dv$=m3 zIPtN*Z*;n{kQE^O2hj?aDXrd78;n9<`uia6p6zqeD~^S;{8MfDd(`IoKv_hwuJ1U3 z&%L{;*GVV-*Ozu+klJ(N)nUusgVYfaA^DA>J?9hwJV zfCEiiSz)gP90k@z?ds!`-(TB0?FH@TGq)4}L28u2T)}`)ms&wLTE% zIuaxxZ~f?av{-4H|KoIRE9Sk;k@Q91_J=A2P;u2#_S`DJ61NzEA8@_r0dOkRi+PoXU^yuIWkGu8bV>UhNVRQ6xg zuYfyz`#8&G*K_%AGL^^73F&_4PqDu#3oeW0n}}o^^TkWMptx7mc9Jz@9`B5OUL7nB zt*m~sY5v>fb+v@DQ0Ls9lvO?Ra>H%zH?<=vOIDH}Vg-jwupU7&Zi6rZVUqZ_;)P+N z@qjT25#SxPE*n1+Y-A>E=H#r_c%7$t;?GCgRrK#XG5TTh1xZ6`v6?oX@xgr__1eW; z9%yhe)IjV7*atT1tCa-o4RnR#`D-Fo{w*F|x1WwjMY4ByRsR2%nY&4* zCx>5d=W1^IM;gf^%qe#bcH(?3Uj02pmU{#HJSQ;cj<#jqF%>uLRuSyTDW9E9nPYpG zW$ma)c>I@FFUfnu@G>Fy|33dAvC!Jr&dYM|Zl?8|ZFP;`Ab)hB;Y+Ca30Xyx8+V_f zBQL8b&Yv3iu+eCTM?3k=ZeaP(7cSz`$KiJ8Lti}qxaage&bs3sP*wYy`~R(5^c7kD z$-(q3ohLM{g7Txo!5PgQVSfDxth`q)^{r^nUfqb%;``If~H1b==w%~ zp7BBK#>f7&tavMFtxbin$Pvby3#q_S&9~>nr7XnwE#y{t)c+~2bNt#agjUxw#hk*G z>;#Ke1Bn!_@#VkR6~XB2vnRpvQ%4}y(`wbx5`K_I*8PFz=9Jyv>x}cf@>4aTtEImT z3x9}uM-7$M|3pZr0T$F0ctoc&3m00l&-{$Ba}RN6Yl&ue$XO49I*?vw3I&vsejr_u zAmeN#B_@T&Q7H&W3ql#lMuDqsh<;~zyYPhn{5q4O2s)MTpo;Iq)6&yOl$!B7bB`b1 z=IL3jdBd*_Pvf6_|Du&ImLA++(UJf83#!zf*Y&uf&vYi1@I;9{rRx&eLjNO`{Mkc% zvm)TzST26rYx1;VDKT$2556T2jbje3Oov&H!$&%V#jB%L5vjMLNPs`rwl}JZ{e$}H zgevDthKXjg+sKt!VqE_oB|XE>r{Cf^SecoucY~H=)b`(r} zmPc&R@p}aI8y>4< z@Z5(eG#;T3bhuNbDf4);xO+LXC7wK`0dlhm*jze;MG<+VbeUm~6P^w)!7wcIE%>KF z%9U-I4}*A(zW;8YWu9e{R!=*^9PStUWNnYcKgE9e*HT<E! z8pP-X*PoQ$9Iw-(7&{QU2}^ZdOOR|d{9sH&q5}A6%A8SoJ(>TzpM388Q3DqL#yMX< z-5M^OmsZ)nOy?lmeAKVARfnyM26l4mY#ba8$0mzTe~TJa)&14Ols)o0(K@yl5=>=m zn@exF^W}Q`SA<|^&TB@tUb1|iTJ^WDdo80PVS=1zF@4kV43Xq@kt3F5!vKh2E>E}r zhX6j*kC<}=_8}iaJ^*l#i3_j$nu z-SLbMV6-8Ro8BMD?#E(Zt&UB)Zud#wJ2D=I*V$)&iQA%ADE=XUL(M=W;ufRVp3Ja? z(h9Ba)FcYgL$k7k6#&2Z0gMzT{C1+ysLAP-7%`y=2`W64`}>IYyz?-8&|k^r?@V)O z&F^I%k#bav*Kc$G9X)Xc+^fhxH@@?eSCiQaY|gp1ZDWx=t*z@U-B-#pdlN-d;4cb` zkLpu38l@*mU z5*WYO60aVN6-~Bw;QWQW?uuS6IZM&{0NcqDN6(a%=$yn{T{sY5>RT&PE_M)s)Dd%I zz%eQ-UYO)G?l-2~24zcD{Du0p5c1zHWBn$Z9xj$WjrD=@YwESolF-9NY$V+iN#zb_ zC#oewI=w4YKPr`)|G*rOU1c{3q+R_JGQ+y(|L>@5*{jR*YQbU9^T>SzgMy0g!b3Ge zigbhR&OK*_Lze{j;~49p-jnzhrEvRznOdEV7vUgdc2ECJsgqptvuQjH4ug>{Sn8uX z4@wWxLp#)?)2O$9oBG%vxD&}@TK)`~W773utoaR(69h!ou6Cp7SUdJ} zr_!3Iotr*fy5F~Lxtvxr@VVXGjNwSk`uAQxm(`dgM$dlH@1jNXHN6g6g(=%?jA2m- z-L{yGMSvYVDNBUkqa`3%QZWlFBp<)Xau=0#H=|EsU#3@hou`(FfEw7Ll+Q{HffM<2 zh1OOFO6}or=Z^=C7s7tbk%-HZh++tpQ_O0FMt`s@C!J^FEmM^K9@ksu;y?`uF$PP4 zKfQwDA3%o2JqxHLnN58yz1}aDpwS?S`>+ZEu zWV=*Vj_`?-r^?w<_(n?|>5sadix{&o&a_lIEZj(h5hqX)DSW$zPNNBh?v>Gno9^dE zm{ZK#2i?mi7BvCY?)`2SW5_@_Mi*EB7n z@ZhU!+uFlKk@?b`O_YJ?ydlY)_)HIW#ZX)hNeBmKGhRZEU!D$ZhRoD;kJ$Z>K~5N`RAjQr?VX2IQ>4)*c+5Iwtg;4 zmz?qLYm_;4dQ-}qwUJEWE&#F)YA1$Ox2!JNFvp^@vulDse~*ahfKZ(5qhMH_DiME6;`2(Gsj?18Z^Do9g!N zXRGr}qK<*!&2HTAzyBL4T4Vr8T=CNNT13*%gHJx2-DY%O=rpHo)$igB_=^wls!b$L z#h~%5r_Un@a9K8#j##Q9=Rzzp*ZS^N_u^EAQZ&Ed>eZIWF&Bc4L3>q@zM^lEcoeBH%0zA+Yt`%;c>I{B^Zr0Uw0A2&X8 zS$tI{)9lvaX3P7x_9gq3=brr|Jlp-yy0^WHUYPwp3tVm>B*tcUdj8A2n}2U4JU(`F z!{2+cKi@QOK9k4#%YD*=|Lf;kyCn*+9WanO>a-w5KqpIFe0OJwKuj_F$A965lg$gN z`TxDOKP=9`pwND2P3?cX_MgRnzQ^ZnPwgr6dU;!PWlYk|*M0d+3?muX3_ { ); }; -const AppWithProviders = () => { +export const Widget = () => { preload(TRADING_VIEW_WIDGET_SCRIPT_URL, { as: "script" }); return ( @@ -52,5 +44,3 @@ const AppWithProviders = () => { ); }; - -export default AppWithProviders; diff --git a/src/components/modules/Account/Deposit/index.tsx b/packages/widget/src/components/modules/Account/Deposit/index.tsx similarity index 80% rename from src/components/modules/Account/Deposit/index.tsx rename to packages/widget/src/components/modules/Account/Deposit/index.tsx index 117d17b..138107a 100644 --- a/src/components/modules/Account/Deposit/index.tsx +++ b/packages/widget/src/components/modules/Account/Deposit/index.tsx @@ -1,5 +1,21 @@ import { Result } from "@effect-atom/atom-react"; import { Navigate } from "@tanstack/react-router"; +import { + Button, + PercentageSlider, + Skeleton, + Text, +} from "@yieldxyz/perps-common/components"; +import type { + TokenBalance, + WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { formatTokenAmount } from "@yieldxyz/perps-common/lib"; +import type { ApiSchemas } from "@yieldxyz/perps-common/services"; +import { BackButton } from "../../../molecules/navigation/back-button"; +import { WalletProtectedRoute } from "../../../molecules/navigation/wallet-protected-route"; +import { ProviderSelect } from "../../../molecules/provider-select"; +import { TokenBalanceSelect } from "../../../molecules/token-balances-select"; import { DepositForm, useDepositForm, @@ -9,18 +25,7 @@ import { useSelectedTokenBalance, useTokenAmountValue, useTokenBalances, -} from "@/components/modules/Account/Deposit/state"; -import { BackButton } from "@/components/molecules/navigation/back-button"; -import { WalletProtectedRoute } from "@/components/molecules/navigation/wallet-protected-route"; -import { PercentageSlider } from "@/components/molecules/percentage-slider"; -import { ProviderSelect } from "@/components/molecules/provider-select"; -import { TokenBalanceSelect } from "@/components/molecules/token-balances-select"; -import { Button } from "@/components/ui/button"; -import { Skeleton } from "@/components/ui/skeleton"; -import type { TokenBalance } from "@/domain/types"; -import type { WalletConnected } from "@/domain/wallet"; -import { formatTokenAmount } from "@/lib/utils"; -import type { ProviderDto } from "@/services/api-client/api-schemas"; +} from "./state"; function AccountDepositContent({ wallet, @@ -32,9 +37,9 @@ function AccountDepositContent({ }: { wallet: WalletConnected; selectedTokenBalance: TokenBalance; - selectedProvider: ProviderDto; - providers: ReadonlyArray; - setSelectedProvider: (provider: ProviderDto) => void; + selectedProvider: ApiSchemas.ProviderDto; + providers: ReadonlyArray; + setSelectedProvider: (provider: ApiSchemas.ProviderDto) => void; setSelectedTokenBalance: (tokenBalance: TokenBalance) => void; }) { const { tokenAmountValue } = useTokenAmountValue( @@ -51,9 +56,9 @@ function AccountDepositContent({

{/* Protocol Selector */} -

+ Select provider -

+ -

+ {tokenAmountValue} -

+
{/* Token Selector */} @@ -87,19 +92,19 @@ function AccountDepositContent({ -

+ Available:{" "} {formatTokenAmount({ amount: selectedTokenBalance.amount, symbol: selectedTokenBalance.token.symbol, })}{" "} -

+ {/* Slider Section */}
-

+ Deposit: {percentage}% -

+ -

+ Deposit -

+
{/* Content */} diff --git a/src/components/modules/Account/Deposit/sign.tsx b/packages/widget/src/components/modules/Account/Deposit/sign.tsx similarity index 59% rename from src/components/modules/Account/Deposit/sign.tsx rename to packages/widget/src/components/modules/Account/Deposit/sign.tsx index 9ad8f74..e18cf03 100644 --- a/src/components/modules/Account/Deposit/sign.tsx +++ b/packages/widget/src/components/modules/Account/Deposit/sign.tsx @@ -1,4 +1,4 @@ -import { SignTransactionsRoute } from "@/components/molecules/sign"; +import { SignTransactionsRoute } from "../../../molecules/sign"; export function DepositSignRoute() { return ; diff --git a/src/components/modules/Account/Deposit/state.tsx b/packages/widget/src/components/modules/Account/Deposit/state.tsx similarity index 83% rename from src/components/modules/Account/Deposit/state.tsx rename to packages/widget/src/components/modules/Account/Deposit/state.tsx index 35bc4c6..492bad1 100644 --- a/src/components/modules/Account/Deposit/state.tsx +++ b/packages/widget/src/components/modules/Account/Deposit/state.tsx @@ -6,34 +6,44 @@ import { useAtomValue, } from "@effect-atom/atom-react"; import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; +import { + actionAtom, + moralisTokenBalancesAtom, + providersAtom, + type TokenBalances, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { AmountField } from "@yieldxyz/perps-common/components"; +import { + isArbUsdcToken, + isEthNativeToken, + isWalletConnected, + makeToken, + type TokenBalance, + type WalletAccount, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { + calcBaseAmountFromUsd, + clampPercent, + formatTokenAmount, + percentOf, + round, + valueFromPercent, +} from "@yieldxyz/perps-common/lib"; +import { + ApiClientService, + type ApiTypes, + runtimeAtom, +} from "@yieldxyz/perps-common/services"; import { Array as _Array, - Number as _Number, Effect, Option, Predicate, Record, Schema, } from "effect"; -import { actionAtom } from "@/atoms/actions-atoms"; -import { providersAtom } from "@/atoms/providers-atoms"; -import { - moralisTokenBalancesAtom, - type TokenBalances, -} from "@/atoms/tokens-atoms"; -import { walletAtom } from "@/atoms/wallet-atom"; -import { AmountField } from "@/components/molecules/forms"; -import { isArbUsdcToken, isEthNativeToken, makeToken } from "@/domain/tokens"; -import type { TokenBalance } from "@/domain/types"; -import { - isWalletConnected, - type WalletAccount, - type WalletConnected, -} from "@/domain/wallet"; -import { formatTokenAmount, truncateNumber } from "@/lib/utils"; -import { ApiClientService } from "@/services/api-client"; -import type { ProviderDto } from "@/services/api-client/api-schemas"; -import { runtimeAtom } from "@/services/runtime"; const selectedTokenBalanceAtom = Atom.family( (walletAddress: WalletAccount["address"]) => @@ -52,7 +62,7 @@ const selectedProviderAtom = Atom.writable( ctx .get(providersAtom) .pipe(Result.map(_Array.head), Result.map(Option.getOrNull)), - (ctx, value: ProviderDto) => ctx.setSelf(Result.success(value)), + (ctx, value: ApiTypes.ProviderDto) => ctx.setSelf(Result.success(value)), ); export const useProviders = () => { @@ -147,7 +157,10 @@ export const depositFormBuilder = FormBuilder.empty return { path: ["Amount"], message: "Missing token balance" }; } - const cryptoAmount = values.Amount / tokenBalance.price; + const cryptoAmount = calcBaseAmountFromUsd({ + usdAmount: values.Amount, + priceUsd: tokenBalance.price, + }); const usdMin = isArbUsdcToken(makeToken(tokenBalance.token)) ? 5 : 10; @@ -185,7 +198,10 @@ export const DepositForm = FormReact.make(depositFormBuilder, { return yield* Effect.dieMessage("No selected provider"); } - const cryptoAmount = decoded.Amount / selectedTokenBalance.price; + const cryptoAmount = calcBaseAmountFromUsd({ + usdAmount: decoded.Amount, + priceUsd: selectedTokenBalance.price, + }); const action = yield* client.ActionsControllerExecuteAction({ providerId: selectedProvider.id, @@ -206,7 +222,7 @@ export const DepositForm = FormReact.make(depositFormBuilder, { }), }); -const amountAtom = DepositForm.getFieldAtom(DepositForm.fields.Amount); +const amountAtom = DepositForm.getFieldValue(DepositForm.fields.Amount); const setAmountFieldAtom = DepositForm.setValue(DepositForm.fields.Amount); export const useDepositPercentage = ( @@ -229,8 +245,8 @@ export const useDepositPercentage = ( Option.getOrElse(() => 0), ); - const percentage = _Number.clamp({ minimum: 0, maximum: 100 })( - (amount / availableBalanceUsd) * 100, + const percentage = clampPercent( + percentOf({ part: amount, whole: availableBalanceUsd }), ); const handlePercentageChange = (newPercentage: number) => { @@ -238,8 +254,11 @@ export const useDepositPercentage = ( return setAmount(availableBalanceUsd.toString()); } - const amount = (availableBalanceUsd * newPercentage) / 100; - setAmount(truncateNumber(amount).toString()); + const amount = valueFromPercent({ + total: availableBalanceUsd, + percent: newPercentage, + }); + setAmount(round(amount).toString()); }; return { @@ -269,7 +288,10 @@ const tokenAmountValueAtom = Atom.family( } return formatTokenAmount({ - amount: amount / tokenBalance.price, + amount: calcBaseAmountFromUsd({ + usdAmount: amount, + priceUsd: tokenBalance.price, + }), symbol: tokenBalance.token.symbol, }); }), diff --git a/src/components/modules/Account/Withdraw/index.tsx b/packages/widget/src/components/modules/Account/Withdraw/index.tsx similarity index 79% rename from src/components/modules/Account/Withdraw/index.tsx rename to packages/widget/src/components/modules/Account/Withdraw/index.tsx index 43e6a00..ea6b97f 100644 --- a/src/components/modules/Account/Withdraw/index.tsx +++ b/packages/widget/src/components/modules/Account/Withdraw/index.tsx @@ -1,5 +1,17 @@ import { Result } from "@effect-atom/atom-react"; import { Navigate } from "@tanstack/react-router"; +import { + Button, + PercentageSlider, + Skeleton, + Text, +} from "@yieldxyz/perps-common/components"; +import type { WalletConnected } from "@yieldxyz/perps-common/domain"; +import { formatAmount, formatTokenAmount } from "@yieldxyz/perps-common/lib"; +import type { ApiSchemas } from "@yieldxyz/perps-common/services"; +import { BackButton } from "../../../molecules/navigation/back-button"; +import { WalletProtectedRoute } from "../../../molecules/navigation/wallet-protected-route"; +import { ProviderSelect } from "../../../molecules/provider-select"; import { useProviderBalance, useProviders, @@ -7,19 +19,7 @@ import { useWithdrawForm, useWithdrawPercentage, WithdrawForm, -} from "@/components/modules/Account/Withdraw/state"; -import { BackButton } from "@/components/molecules/navigation/back-button"; -import { WalletProtectedRoute } from "@/components/molecules/navigation/wallet-protected-route"; -import { PercentageSlider } from "@/components/molecules/percentage-slider"; -import { ProviderSelect } from "@/components/molecules/provider-select"; -import { Button } from "@/components/ui/button"; -import { Skeleton } from "@/components/ui/skeleton"; -import type { WalletConnected } from "@/domain/wallet"; -import { formatAmount, formatTokenAmount } from "@/lib/utils"; -import type { - BalanceDto, - ProviderDto, -} from "@/services/api-client/api-schemas"; +} from "./state"; function AccountWithdrawContent({ wallet, @@ -29,10 +29,10 @@ function AccountWithdrawContent({ setSelectedProvider, }: { wallet: WalletConnected; - selectedProvider: ProviderDto; - providers: ReadonlyArray; - providerBalance: BalanceDto; - setSelectedProvider: (provider: ProviderDto) => void; + selectedProvider: ApiSchemas.ProviderDto; + providers: ReadonlyArray; + providerBalance: ApiSchemas.BalanceDto; + setSelectedProvider: (provider: ApiSchemas.ProviderDto) => void; }) { const { percentage, handlePercentageChange } = useWithdrawPercentage(wallet); @@ -40,9 +40,9 @@ function AccountWithdrawContent({
{/* Provider Selector */} -

+ Select provider -

+ -

+ Available:{" "} {formatTokenAmount({ amount: providerBalance.availableBalance, symbol: providerBalance.collateral.symbol, })} -

+
{/* Slider Section */}
-

+ Withdraw: {percentage}% -

+ -

+ Withdraw -

+
{/* Content */} diff --git a/src/components/modules/Account/Withdraw/sign.tsx b/packages/widget/src/components/modules/Account/Withdraw/sign.tsx similarity index 59% rename from src/components/modules/Account/Withdraw/sign.tsx rename to packages/widget/src/components/modules/Account/Withdraw/sign.tsx index cfb0eb5..db311bf 100644 --- a/src/components/modules/Account/Withdraw/sign.tsx +++ b/packages/widget/src/components/modules/Account/Withdraw/sign.tsx @@ -1,4 +1,4 @@ -import { SignTransactionsRoute } from "@/components/molecules/sign"; +import { SignTransactionsRoute } from "../../../molecules/sign"; export function WithdrawSignRoute() { return ; diff --git a/src/components/modules/Account/Withdraw/state.tsx b/packages/widget/src/components/modules/Account/Withdraw/state.tsx similarity index 81% rename from src/components/modules/Account/Withdraw/state.tsx rename to packages/widget/src/components/modules/Account/Withdraw/state.tsx index 1ff5fa1..03ab238 100644 --- a/src/components/modules/Account/Withdraw/state.tsx +++ b/packages/widget/src/components/modules/Account/Withdraw/state.tsx @@ -7,29 +7,35 @@ import { } from "@effect-atom/atom-react"; import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; import { - Array as _Array, - Number as _Number, - Effect, - Option, - Schema, -} from "effect"; -import { actionAtom } from "@/atoms/actions-atoms"; -import { selectedProviderBalancesAtom } from "@/atoms/portfolio-atoms"; -import { providersAtom } from "@/atoms/providers-atoms"; -import { walletAtom } from "@/atoms/wallet-atom"; -import { AmountField } from "@/components/molecules/forms"; -import { isWalletConnected, type WalletConnected } from "@/domain/wallet"; -import { truncateNumber } from "@/lib/utils"; -import { ApiClientService } from "@/services/api-client"; -import type { ProviderDto } from "@/services/api-client/api-schemas"; -import { runtimeAtom } from "@/services/runtime"; + actionAtom, + providersAtom, + selectedProviderBalancesAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { AmountField } from "@yieldxyz/perps-common/components"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { + clampPercent, + percentOf, + round, + valueFromPercent, +} from "@yieldxyz/perps-common/lib"; +import { + ApiClientService, + type ApiTypes, + runtimeAtom, +} from "@yieldxyz/perps-common/services"; +import { Array as _Array, Effect, Option, Schema } from "effect"; const selectedProviderAtom = Atom.writable( (ctx) => ctx .get(providersAtom) .pipe(Result.map(_Array.head), Result.map(Option.getOrNull)), - (ctx, value: ProviderDto) => ctx.setSelf(Result.success(value)), + (ctx, value: ApiTypes.ProviderDto) => ctx.setSelf(Result.success(value)), ); export const useProviders = () => { @@ -155,7 +161,7 @@ export const WithdrawForm = FormReact.make(withdrawFormBuilder, { }); const setAmountFieldAtom = WithdrawForm.setValue(WithdrawForm.fields.Amount); -const amountFieldAtom = WithdrawForm.getFieldAtom(WithdrawForm.fields.Amount); +const amountFieldAtom = WithdrawForm.getFieldValue(WithdrawForm.fields.Amount); export const useSetWithdrawAmount = () => { const setAmount = useAtomSet(setAmountFieldAtom); @@ -187,12 +193,15 @@ export const useWithdrawPercentage = (wallet: WalletConnected) => { return setAmount(availableBalance.toString()); } - const amount = (availableBalance * newPercentage) / 100; - setAmount(truncateNumber(amount, 6).toString()); + const amount = valueFromPercent({ + total: availableBalance, + percent: newPercentage, + }); + setAmount(round(amount, 6).toString()); }; - const percentage = _Number.clamp({ minimum: 0, maximum: 100 })( - (amount / availableBalance) * 100, + const percentage = clampPercent( + percentOf({ part: amount, whole: availableBalance }), ); return { diff --git a/src/components/modules/Account/balance.tsx b/packages/widget/src/components/modules/Account/balance.tsx similarity index 78% rename from src/components/modules/Account/balance.tsx rename to packages/widget/src/components/modules/Account/balance.tsx index ffa5d5a..899798e 100644 --- a/src/components/modules/Account/balance.tsx +++ b/packages/widget/src/components/modules/Account/balance.tsx @@ -1,15 +1,17 @@ import { Result, useAtomValue } from "@effect-atom/atom-react"; import { useNavigate } from "@tanstack/react-router"; +import hyperliquid from "@yieldxyz/perps-common/assets/hyperliquid.png"; +import { + providersAtom, + providersBalancesAtom, +} from "@yieldxyz/perps-common/atoms"; +import { Button, Text } from "@yieldxyz/perps-common/components"; +import type { WalletConnected } from "@yieldxyz/perps-common/domain"; +import { formatAmount } from "@yieldxyz/perps-common/lib"; import { Option, Record } from "effect"; -import hyperliquid from "@/assets/hyperliquid.png"; -import { providersBalancesAtom } from "@/atoms/portfolio-atoms"; -import { providersAtom } from "@/atoms/providers-atoms"; -import { AccountValueDisplay } from "@/components/molecules/account-value-display"; -import { BackButton } from "@/components/molecules/navigation/back-button"; -import { WalletProtectedRoute } from "@/components/molecules/navigation/wallet-protected-route"; -import { Button } from "@/components/ui/button"; -import type { WalletConnected } from "@/domain/wallet"; -import { formatAmount } from "@/lib/utils"; +import { AccountValueDisplay } from "../../molecules/account-value-display"; +import { BackButton } from "../../molecules/navigation/back-button"; +import { WalletProtectedRoute } from "../../molecules/navigation/wallet-protected-route"; export function AccountBalancesWithWallet({ wallet, @@ -39,9 +41,9 @@ export function AccountBalancesWithWallet({
-

+ Balances -

+
{/* Account Value Card */} @@ -52,9 +54,9 @@ export function AccountBalancesWithWallet({ {/* Accounts Balance Section */}
-

+ Accounts Balance -

+ {data .onSuccess((val) => @@ -77,9 +79,9 @@ export function AccountBalancesWithWallet({ className="size-9 rounded-full" />
-

+ {provider?.name} -

+ {/*
{formatPrice(balance.usedMargin)} @@ -87,9 +89,9 @@ export function AccountBalancesWithWallet({
*/}
-

+ {formatAmount(balance.accountValue)} -

+ {/*

Avail: {formatPrice(balance.availableBalance)}

*/} diff --git a/src/components/modules/Home/AssetList/index.tsx b/packages/widget/src/components/modules/Home/AssetList/index.tsx similarity index 86% rename from src/components/modules/Home/AssetList/index.tsx rename to packages/widget/src/components/modules/Home/AssetList/index.tsx index 28b1099..2b2fcda 100644 --- a/src/components/modules/Home/AssetList/index.tsx +++ b/packages/widget/src/components/modules/Home/AssetList/index.tsx @@ -1,6 +1,8 @@ import { Result, useAtomValue } from "@effect-atom/atom-react"; import { useVirtualizer } from "@tanstack/react-virtual"; -import { Record } from "effect"; +import { marketsAtom, walletAtom } from "@yieldxyz/perps-common/atoms"; +import { AssetSkeleton, Text } from "@yieldxyz/perps-common/components"; +import { Array as _Array, Option, Record } from "effect"; import { ArrowDownUp, ArrowDownWideNarrow, @@ -9,10 +11,7 @@ import { X, } from "lucide-react"; import { useRef, useState } from "react"; -import { marketsAtom } from "@/atoms/markets-atoms"; -import { walletAtom } from "@/atoms/wallet-atom"; -import { AssetItem } from "@/components/modules/Home/AssetList/item"; -import { AssetSkeleton } from "@/components/ui/skeleton"; +import { AssetItem } from "./item"; export function AssetList() { const [searchQuery, setSearchQuery] = useState(""); @@ -102,12 +101,12 @@ export function AssetList() { {hasNoResults ? (
-

+ This asset isn't available on Hyperliquid -

-

+ + Please switch the provider to continue -

+
diff --git a/src/components/modules/Home/Positions/index.tsx b/packages/widget/src/components/modules/Home/Positions/index.tsx similarity index 78% rename from src/components/modules/Home/Positions/index.tsx rename to packages/widget/src/components/modules/Home/Positions/index.tsx index a2fa436..b081ac2 100644 --- a/src/components/modules/Home/Positions/index.tsx +++ b/packages/widget/src/components/modules/Home/Positions/index.tsx @@ -1,19 +1,22 @@ import { Result, useAtomValue } from "@effect-atom/atom-react"; -import { Array as _Array, Match, Option, Record } from "effect"; -import { useState } from "react"; -import { marketsAtom } from "@/atoms/markets-atoms"; import { + marketsAtom, ordersAtom, positionsAtom, selectedProviderBalancesAtom, -} from "@/atoms/portfolio-atoms"; -import { walletAtom } from "@/atoms/wallet-atom"; -import { OrderCard } from "@/components/modules/Home/Positions/order-card"; -import { PositionCard } from "@/components/modules/Home/Positions/position-card"; -import { Skeleton } from "@/components/ui/skeleton"; -import { isWalletConnected, type WalletConnected } from "@/domain/wallet"; -import { formatAmount, formatPercentage } from "@/lib/utils"; -import type { OrderDto } from "@/services/api-client/client-factory"; + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { Skeleton, Text } from "@yieldxyz/perps-common/components"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { formatAmount, formatPercentage } from "@yieldxyz/perps-common/lib"; +import type { ApiSchemas } from "@yieldxyz/perps-common/services"; +import { Array as _Array, Match, Option, Record } from "effect"; +import { useState } from "react"; +import { OrderCard } from "./order-card"; +import { PositionCard } from "./position-card"; function PositionsWithWallet({ wallet }: { wallet: WalletConnected }) { const [activeTab, setActiveTab] = useState<"positions" | "orders">( @@ -89,7 +92,7 @@ function PositionsWithWallet({ wallet }: { wallet: WalletConnected }) { marketRef: m, position: p, orders: Record.get(ordersMap, p.marketId).pipe( - Option.getOrElse(() => [] as OrderDto[]), + Option.getOrElse(() => [] as ApiSchemas.OrderDto[]), ), })), ), @@ -114,12 +117,12 @@ function PositionsWithWallet({ wallet }: { wallet: WalletConnected }) { if (!totalsData) { return (
-

+ Unable to load data -

-

+ + Please try again later -

+
); } @@ -130,31 +133,31 @@ function PositionsWithWallet({ wallet }: { wallet: WalletConnected }) {
{/* Total value section */}
- + {formatAmount(totalsData.accountValue)} - +
- {isPnlPositive ? "+" : ""} {formatAmount(totalsData.unrealizedPnl)} - - + {isPnlPositive ? "+" : ""} {formatPercentage(totalsData.pnlPercent)} - +
{/* Positions/Orders tabs */} -
+
@@ -296,7 +309,7 @@ function MarketOrderContent({ mode, }: { wallet: WalletConnected; - side: PositionDtoSide; + side: ApiTypes.PositionDtoSide; mode: OrderMode; }) { const { marketId } = useParams({ strict: false }) as { marketId: string }; @@ -324,7 +337,7 @@ export function MarketOrder({ side, mode = "open", }: { - side: PositionDtoSide; + side: ApiTypes.PositionDtoSide; mode: OrderMode; }) { return ( diff --git a/src/components/modules/Order/Overview/loading.tsx b/packages/widget/src/components/modules/Order/Overview/loading.tsx similarity index 92% rename from src/components/modules/Order/Overview/loading.tsx rename to packages/widget/src/components/modules/Order/Overview/loading.tsx index 71a0657..4c44198 100644 --- a/src/components/modules/Order/Overview/loading.tsx +++ b/packages/widget/src/components/modules/Order/Overview/loading.tsx @@ -1,5 +1,5 @@ -import { SLIDER_STOPS } from "@/components/modules/Order/Overview/state"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Skeleton } from "@yieldxyz/perps-common/components"; +import { SLIDER_STOPS } from "./state"; export function OrderLoading() { return ( diff --git a/src/components/modules/Order/Overview/state.tsx b/packages/widget/src/components/modules/Order/Overview/state.tsx similarity index 83% rename from src/components/modules/Order/Overview/state.tsx rename to packages/widget/src/components/modules/Order/Overview/state.tsx index 684147c..1fe3dac 100644 --- a/src/components/modules/Order/Overview/state.tsx +++ b/packages/widget/src/components/modules/Order/Overview/state.tsx @@ -6,28 +6,39 @@ import { useAtomValue, } from "@effect-atom/atom-react"; import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; -import { Number as _Number, Effect, Option, Schema } from "effect"; -import { actionAtom } from "@/atoms/actions-atoms"; -import { selectedProviderBalancesAtom } from "@/atoms/portfolio-atoms"; -import { selectedProviderAtom } from "@/atoms/providers-atoms"; -import { walletAtom } from "@/atoms/wallet-atom"; -import { AmountField } from "@/components/molecules/forms"; -import type { TPOrSLSettings } from "@/components/molecules/tp-sl-dialog"; -import { getMaxLeverage } from "@/domain/market"; import { + actionAtom, + selectedProviderAtom, + selectedProviderBalancesAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { + AmountField, + type TPOrSLSettings, +} from "@yieldxyz/perps-common/components"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { + calcBaseAmountFromUsd, calculateMargin, + calculatePositionSize, + clampPercent, getLiquidationPrice, + getMaxLeverage, MIN_LEVERAGE, -} from "@/domain/position"; -import { isWalletConnected, type WalletConnected } from "@/domain/wallet"; -import { truncateNumber } from "@/lib/utils"; -import { ApiClientService } from "@/services/api-client"; + percentOf, + round, + valueFromPercent, +} from "@yieldxyz/perps-common/lib"; import { - type ArgumentsDto, - MarketDto, -} from "@/services/api-client/api-schemas"; -import type { PositionDtoSide } from "@/services/api-client/client-factory"; -import { runtimeAtom } from "@/services/runtime"; + ApiClientService, + ApiSchemas, + type ApiTypes, + runtimeAtom, +} from "@yieldxyz/perps-common/services"; +import { Number as _Number, Effect, Option, Schema } from "effect"; export type OrderType = "market" | "limit"; @@ -39,7 +50,7 @@ const orderTypeAtom = Atom.writable( ); export const LeverageRangesSchema = Schema.Data( - MarketDto.fields.leverageRange, + ApiSchemas.MarketDto.fields.leverageRange, ).pipe(Schema.brand("LeverageRange")); const leverageAtom = Atom.family( @@ -183,8 +194,8 @@ export const formAtom = Atom.family( side, }: { wallet: WalletConnected; - market: MarketDto; - side: PositionDtoSide; + market: ApiSchemas.MarketDto; + side: ApiTypes.PositionDtoSide; }, { decoded }, ) => @@ -204,13 +215,13 @@ export const formAtom = Atom.family( const orderType = registry.get(orderTypeAtom); const tpOrSLSettings = registry.get(tpOrSLSettingsAtom); - const stopLossPrice: ArgumentsDto["stopLossPrice"] = + const stopLossPrice: ApiTypes.ArgumentsDto["stopLossPrice"] = tpOrSLSettings.stopLoss.triggerPrice && tpOrSLSettings.stopLoss.option !== null ? tpOrSLSettings.stopLoss.triggerPrice : undefined; - const takeProfitPrice: ArgumentsDto["takeProfitPrice"] = + const takeProfitPrice: ApiTypes.ArgumentsDto["takeProfitPrice"] = tpOrSLSettings.takeProfit.triggerPrice && tpOrSLSettings.takeProfit.option !== null ? tpOrSLSettings.takeProfit.triggerPrice @@ -250,7 +261,7 @@ export const formAtom = Atom.family( }; const setAmountFieldAtom = OrderForm.setValue(OrderForm.fields.Amount); - const amountFieldAtom = OrderForm.getFieldAtom(OrderForm.fields.Amount); + const amountFieldAtom = OrderForm.getFieldValue(OrderForm.fields.Amount); const useHandlePercentageChange = (wallet: WalletConnected) => { const setAmount = useAtomSet(setAmountFieldAtom); @@ -261,15 +272,17 @@ export const formAtom = Atom.family( handlePercentageChange: (value: number) => { if (!providerBalance) return; - const clampedValue = _Number.clamp({ minimum: 0, maximum: 100 })( - value, - ); - - const marginToUse = - (clampedValue / 100) * providerBalance.availableBalance; - const positionSize = marginToUse * leverage; + const clampedValue = clampPercent(value); + const marginToUse = valueFromPercent({ + total: providerBalance.availableBalance, + percent: clampedValue, + }); + const positionSize = calculatePositionSize({ + margin: marginToUse, + leverage, + }); - setAmount(truncateNumber(positionSize).toString()); + setAmount(round(positionSize).toString()); }, }; }; @@ -307,24 +320,28 @@ export const formAtom = Atom.family( } const margin = calculateMargin({ positionSize: amount, leverage }); - const percentage = - Math.min( - 100, - Math.max(0, (margin / providerBalance.availableBalance) * 100), - ) || 0; + const percentage = clampPercent( + percentOf({ part: margin, whole: providerBalance.availableBalance }), + ); return { percentage: Math.round(percentage), }; }; - const useOrderCalculations = (market: MarketDto, side: PositionDtoSide) => { + const useOrderCalculations = ( + market: ApiSchemas.MarketDto, + side: ApiTypes.PositionDtoSide, + ) => { const { amount } = useOrderAmount(); const { leverage } = useLeverage( Schema.decodeSync(LeverageRangesSchema)(market.leverageRange), ); - const cryptoAmount = amount / market.markPrice; + const cryptoAmount = calcBaseAmountFromUsd({ + usdAmount: amount, + priceUsd: market.markPrice, + }); const liquidationPrice = getLiquidationPrice({ currentPrice: market.markPrice, diff --git a/src/components/modules/Order/sign.tsx b/packages/widget/src/components/modules/Order/sign.tsx similarity index 57% rename from src/components/modules/Order/sign.tsx rename to packages/widget/src/components/modules/Order/sign.tsx index ab2f793..76f7102 100644 --- a/src/components/modules/Order/sign.tsx +++ b/packages/widget/src/components/modules/Order/sign.tsx @@ -1,4 +1,4 @@ -import { SignTransactionsRoute } from "@/components/molecules/sign"; +import { SignTransactionsRoute } from "../../molecules/sign"; export function OrderSignRoute() { return ; diff --git a/src/components/modules/PositionDetails/Close/index.tsx b/packages/widget/src/components/modules/PositionDetails/Close/index.tsx similarity index 71% rename from src/components/modules/PositionDetails/Close/index.tsx rename to packages/widget/src/components/modules/PositionDetails/Close/index.tsx index 95e62fe..d8e63f0 100644 --- a/src/components/modules/PositionDetails/Close/index.tsx +++ b/packages/widget/src/components/modules/PositionDetails/Close/index.tsx @@ -1,22 +1,29 @@ import { Result } from "@effect-atom/atom-react"; import { Navigate, useParams } from "@tanstack/react-router"; +import { + Button, + Divider, + PercentageSlider, + Skeleton, + Text, +} from "@yieldxyz/perps-common/components"; +import type { WalletConnected } from "@yieldxyz/perps-common/domain"; +import { + formatAmount, + formatPercentage, + formatTokenAmount, + getPositionChangePercent, +} from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; +import { BackButton } from "../../../molecules/navigation/back-button"; +import { WalletProtectedRoute } from "../../../molecules/navigation/wallet-protected-route"; import { SLIDER_STOPS, useCloseCalculations, useClosePercentage, usePosition, useSubmitClose, -} from "@/components/modules/PositionDetails/Close/state"; -import { BackButton } from "@/components/molecules/navigation/back-button"; -import { WalletProtectedRoute } from "@/components/molecules/navigation/wallet-protected-route"; -import { PercentageSlider } from "@/components/molecules/percentage-slider"; -import { Button } from "@/components/ui/button"; -import { Divider } from "@/components/ui/divider"; -import { Skeleton } from "@/components/ui/skeleton"; -import { getPositionChangePercent } from "@/domain/position"; -import type { WalletConnected } from "@/domain/wallet"; -import { formatAmount, formatPercentage, formatTokenAmount } from "@/lib/utils"; -import type { PositionDto } from "@/services/api-client/api-schemas"; +} from "./state"; function ClosePositionLoading() { return ( @@ -68,7 +75,7 @@ function ClosePositionContent({ position, }: { wallet: WalletConnected; - position: PositionDto; + position: ApiTypes.PositionDto; }) { const { closePercentage, setClosePercentage } = useClosePercentage(); const calculations = useCloseCalculations(position); @@ -88,22 +95,22 @@ function ClosePositionContent({
- + Close amount - +
- + {formatAmount(position.markPrice)} - - + {isPositive ? "+" : ""} {formatPercentage(priceChangePercent)} - +
@@ -112,18 +119,18 @@ function ClosePositionContent({
{/* Amount Display */}
-

+ Select amount to close -

-

+ + {formatAmount(calculations.closeValue)} -

-

+ + {formatTokenAmount({ amount: calculations.closeSize, symbol: "Size:", })} -

+
{/* Slider */} @@ -138,21 +145,23 @@ function ClosePositionContent({
{/* Margin Row */}
- + Margin - +
- + {formatAmount(calculations.marginReturn)} - - + {isPnlPositive ? "+" : ""} {formatAmount(calculations.pnlReturn)} - +
@@ -160,12 +169,12 @@ function ClosePositionContent({ {/* You will receive Row */}
- + You will receive - - + + {formatAmount(calculations.youWillReceive)} - +
@@ -176,7 +185,8 @@ function ClosePositionContent({ onClick={handleSubmit} loading={submitResult.waiting} disabled={submitResult.waiting || closePercentage === 0} - className="w-full h-14 bg-white text-black hover:bg-white/90 text-base font-semibold" + size="lg" + className="w-full text-base font-semibold bg-white text-black hover:bg-white/90" > {submitResult.waiting ? "Processing..." : "Close position"} diff --git a/src/components/modules/PositionDetails/Close/sign.tsx b/packages/widget/src/components/modules/PositionDetails/Close/sign.tsx similarity index 59% rename from src/components/modules/PositionDetails/Close/sign.tsx rename to packages/widget/src/components/modules/PositionDetails/Close/sign.tsx index 983a448..61010aa 100644 --- a/src/components/modules/PositionDetails/Close/sign.tsx +++ b/packages/widget/src/components/modules/PositionDetails/Close/sign.tsx @@ -1,4 +1,4 @@ -import { SignTransactionsRoute } from "@/components/molecules/sign"; +import { SignTransactionsRoute } from "../../../molecules/sign"; export function CloseSignRoute() { return ; diff --git a/src/components/modules/PositionDetails/Close/state.tsx b/packages/widget/src/components/modules/PositionDetails/Close/state.tsx similarity index 82% rename from src/components/modules/PositionDetails/Close/state.tsx rename to packages/widget/src/components/modules/PositionDetails/Close/state.tsx index 263723b..ed1b4ac 100644 --- a/src/components/modules/PositionDetails/Close/state.tsx +++ b/packages/widget/src/components/modules/PositionDetails/Close/state.tsx @@ -5,15 +5,22 @@ import { useAtomSet, useAtomValue, } from "@effect-atom/atom-react"; +import { + actionAtom, + positionsAtom, + selectedProviderAtom, +} from "@yieldxyz/perps-common/atoms"; +import type { + WalletAccount, + WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { getCloseCalculations } from "@yieldxyz/perps-common/lib"; +import { + ApiClientService, + type ApiTypes, + runtimeAtom, +} from "@yieldxyz/perps-common/services"; import { Number as _Number, Data, Effect } from "effect"; -import { actionAtom } from "@/atoms/actions-atoms"; -import { positionsAtom } from "@/atoms/portfolio-atoms"; -import { selectedProviderAtom } from "@/atoms/providers-atoms"; -import { getCloseCalculations } from "@/domain/position"; -import type { WalletAccount, WalletConnected } from "@/domain/wallet"; -import { ApiClientService } from "@/services/api-client"; -import type { PositionDto } from "@/services/api-client/client-factory"; -import { runtimeAtom } from "@/services/runtime"; export const SLIDER_STOPS = [0, 25, 50, 75, 100]; @@ -59,7 +66,7 @@ export const usePosition = ( ); }; -export const useCloseCalculations = (position: PositionDto) => { +export const useCloseCalculations = (position: ApiTypes.PositionDto) => { const { closePercentage } = useClosePercentage(); return getCloseCalculations(position, closePercentage); @@ -67,7 +74,7 @@ export const useCloseCalculations = (position: PositionDto) => { const submitCloseAtom = runtimeAtom.fn( Effect.fn(function* (args: { - position: PositionDto; + position: ApiTypes.PositionDto; wallet: WalletConnected; }) { const client = yield* ApiClientService; diff --git a/src/components/modules/PositionDetails/Overview/CancelOrder/sign.tsx b/packages/widget/src/components/modules/PositionDetails/Overview/CancelOrder/sign.tsx similarity index 60% rename from src/components/modules/PositionDetails/Overview/CancelOrder/sign.tsx rename to packages/widget/src/components/modules/PositionDetails/Overview/CancelOrder/sign.tsx index 8f18800..970d176 100644 --- a/src/components/modules/PositionDetails/Overview/CancelOrder/sign.tsx +++ b/packages/widget/src/components/modules/PositionDetails/Overview/CancelOrder/sign.tsx @@ -1,4 +1,4 @@ -import { SignTransactionsRoute } from "@/components/molecules/sign"; +import { SignTransactionsRoute } from "../../../../molecules/sign"; export function CancelOrderSignRoute() { return ; diff --git a/src/components/modules/PositionDetails/Overview/EditLeverage/sign.tsx b/packages/widget/src/components/modules/PositionDetails/Overview/EditLeverage/sign.tsx similarity index 61% rename from src/components/modules/PositionDetails/Overview/EditLeverage/sign.tsx rename to packages/widget/src/components/modules/PositionDetails/Overview/EditLeverage/sign.tsx index b0d45fd..604b7ff 100644 --- a/src/components/modules/PositionDetails/Overview/EditLeverage/sign.tsx +++ b/packages/widget/src/components/modules/PositionDetails/Overview/EditLeverage/sign.tsx @@ -1,4 +1,4 @@ -import { SignTransactionsRoute } from "@/components/molecules/sign"; +import { SignTransactionsRoute } from "../../../../molecules/sign"; export function EditLeverageSignRoute() { return ; diff --git a/src/components/modules/PositionDetails/Overview/Orders/index.tsx b/packages/widget/src/components/modules/PositionDetails/Overview/Orders/index.tsx similarity index 68% rename from src/components/modules/PositionDetails/Overview/Orders/index.tsx rename to packages/widget/src/components/modules/PositionDetails/Overview/Orders/index.tsx index 5e224e3..17af430 100644 --- a/src/components/modules/PositionDetails/Overview/Orders/index.tsx +++ b/packages/widget/src/components/modules/PositionDetails/Overview/Orders/index.tsx @@ -1,28 +1,41 @@ import { Result, useAtomSet, useAtomValue } from "@effect-atom/atom-react"; import { Navigate } from "@tanstack/react-router"; -import { cancelOrderAtom } from "@/atoms/orders-pending-actions-atom"; -import { ordersAtom } from "@/atoms/portfolio-atoms"; -import { walletAtom } from "@/atoms/wallet-atom"; -import { Button } from "@/components/ui/button"; -import { Card, CardSection } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; -import { isWalletConnected, type WalletConnected } from "@/domain/wallet"; -import { useOrderActions } from "@/hooks/use-order-actions"; -import { formatAmount, formatDate, formatSnakeCase } from "@/lib/utils"; -import type { MarketDto, OrderDto } from "@/services/api-client/client-factory"; +import { + cancelOrderAtom, + ordersAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { + Button, + Card, + CardSection, + Skeleton, + Text, +} from "@yieldxyz/perps-common/components"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { useOrderActions } from "@yieldxyz/perps-common/hooks"; +import { + calcNotionalUsd, + formatAmount, + formatDate, + formatSnakeCase, +} from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; function OrderCard({ order, market, wallet, }: { - order: OrderDto; - market: MarketDto; + order: ApiTypes.OrderDto; + market: ApiTypes.MarketDto; wallet: WalletConnected; }) { const price = order.limitPrice ?? order.triggerPrice ?? 0; - const sizeNum = Number.parseFloat(order.size); - const value = price * sizeNum; + const value = calcNotionalUsd({ priceUsd: price, sizeBase: order.size }); const { cancelOrderAction } = useOrderActions(order); @@ -32,22 +45,22 @@ function OrderCard({ {/* Order info */}
- + {formatSnakeCase(order.type)} - - + + {formatDate(order.createdAt)} - +
{/* Value and size */}
- + {formatAmount(value)} - - + + {order.size} {market.baseAsset.symbol} - +
@@ -57,32 +70,34 @@ function OrderCard({ className="flex items-start gap-4" >
- + {order.limitPrice ? "Limit" : "Trigger"} - - + + {formatAmount(price)} - +
- + Market - - + + {formatAmount(market.markPrice)} - +
- + Side - - + {order.side === "long" ? "Long" : "Short"} - +
@@ -146,7 +161,7 @@ function OrdersTabContentWithWallet({ market, }: { wallet: WalletConnected; - market: MarketDto; + market: ApiTypes.MarketDto; }) { const ordersResult = useAtomValue(ordersAtom(wallet.currentAccount.address)); @@ -162,7 +177,7 @@ function OrdersTabContentWithWallet({ Result.map((allOrders) => allOrders.filter((o) => o.marketId === market.id), ), - Result.getOrElse(() => [] as OrderDto[]), + Result.getOrElse(() => [] as ApiTypes.OrderDto[]), ); if (orders.length === 0) { @@ -172,9 +187,9 @@ function OrdersTabContentWithWallet({ position="only" className="flex flex-col items-center py-8" > - + No open orders for this market - + ); @@ -194,18 +209,18 @@ function OrdersTabContentWithWallet({ ); } -export function OrdersTabContent({ market }: { market: MarketDto }) { +export function OrdersTabContent({ market }: { market: ApiTypes.MarketDto }) { const wallet = useAtomValue(walletAtom).pipe(Result.getOrElse(() => null)); if (!isWalletConnected(wallet)) { return (
-

+ Wallet not connected -

-

+ + Connect your wallet to see your orders -

+
); } diff --git a/src/components/modules/PositionDetails/Overview/Position/index.tsx b/packages/widget/src/components/modules/PositionDetails/Overview/Position/index.tsx similarity index 71% rename from src/components/modules/PositionDetails/Overview/Position/index.tsx rename to packages/widget/src/components/modules/PositionDetails/Overview/Position/index.tsx index a102574..41182b4 100644 --- a/src/components/modules/PositionDetails/Overview/Position/index.tsx +++ b/packages/widget/src/components/modules/PositionDetails/Overview/Position/index.tsx @@ -1,27 +1,38 @@ import { Result, useAtomValue } from "@effect-atom/atom-react"; import { Link, Navigate } from "@tanstack/react-router"; -import { ordersAtom, positionsAtom } from "@/atoms/portfolio-atoms"; -import { walletAtom } from "@/atoms/wallet-atom"; -import { LeverageDialog } from "@/components/modules/Order/Overview/leverage-dialog"; import { - getTPOrSLConfigurationFromPosition, + ordersAtom, + positionsAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { + Button, + Card, + CardSection, + LeverageDialog, + Skeleton, + Text, TPOrSLDialog, type TPOrSLOption, type TPOrSLSettings, -} from "@/components/molecules/tp-sl-dialog"; -import { Button } from "@/components/ui/button"; -import { Card, CardSection } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; -import { getMaxLeverage } from "@/domain/market"; -import { isWalletConnected, type WalletConnected } from "@/domain/wallet"; -import { usePositionActions } from "@/hooks/use-position-actions"; -import { useTpSlOrders } from "@/hooks/use-tp-sl-orders"; -import { formatAmount, formatPercentage } from "@/lib/utils"; -import type { OrderDto } from "@/services/api-client/api-schemas"; -import type { - MarketDto, - PositionDto, -} from "@/services/api-client/client-factory"; +} from "@yieldxyz/perps-common/components"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; +import { + usePositionActions, + useTpSlOrders, +} from "@yieldxyz/perps-common/hooks"; +import { + calcNotionalUsd, + calcPnlPercent, + formatAmount, + formatPercentage, + getMaxLeverage, + getTPOrSLConfigurationFromPosition, +} from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; import { useEditSLTP, useUpdateLeverage } from "./state"; function PositionCardContent({ @@ -30,9 +41,9 @@ function PositionCardContent({ wallet, orders, }: { - orders: OrderDto[]; - position: PositionDto; - market: MarketDto; + orders: ApiTypes.OrderDto[]; + position: ApiTypes.PositionDto; + market: ApiTypes.MarketDto; wallet: WalletConnected; }) { const { editTPResult, editTP, editSLResult, editSL } = useEditSLTP(); @@ -76,10 +87,14 @@ function PositionCardContent({ }; const symbol = market.baseAsset.symbol; - const sizeNum = Number.parseFloat(position.size); - const value = position.markPrice * sizeNum; - const pnlPercent = - position.margin > 0 ? (position.unrealizedPnl / position.margin) * 100 : 0; + const value = calcNotionalUsd({ + priceUsd: position.markPrice, + sizeBase: position.size, + }); + const pnlPercent = calcPnlPercent({ + pnlUsd: position.unrealizedPnl, + marginUsd: position.margin, + }); const isPnlPositive = position.unrealizedPnl >= 0; if (Result.isSuccess(editTPResult) || Result.isSuccess(editSLResult)) { @@ -105,90 +120,92 @@ function PositionCardContent({ {/* Header Row - Symbol & PnL */}
- + {symbol} {position.leverage}x - - + + {position.size} {symbol} - +
- + {formatAmount(value)} - - + {isPnlPositive ? "+" : ""} {formatAmount(position.unrealizedPnl)} ({isPnlPositive ? "+" : ""} {formatPercentage(pnlPercent)}) - +
{/* Second Row - Entry, Liq. Price, Margin */}
- + Entry - - + + {formatAmount(position.entryPrice)} - +
- + Liq. Price - - + + {formatAmount(position.liquidationPrice)} - +
- + Margin - - + + {formatAmount(position.margin)} - +
{/* Third Row - Take profit, Stop loss, Side */}
- + Take profit - - + + {tpSlOrders.takeProfit?.triggerPrice ? formatAmount(tpSlOrders.takeProfit.triggerPrice) : "Not set"} - +
- + Stop loss - - + + {tpSlOrders.stopLoss?.triggerPrice ? formatAmount(tpSlOrders.stopLoss.triggerPrice) : "Not set"} - +
- + Side - - + {position.side === "long" ? "Long" : "Short"} - +
@@ -212,8 +229,7 @@ function PositionCardContent({ > @@ -73,7 +85,10 @@ function BottomButtonsContent({ params={{ marketId: market.id, side: "short" }} className="flex-1" > - @@ -86,7 +101,7 @@ function BottomButtonsContent({ ); } -function BottomButtons({ market }: { market: MarketDto }) { +function BottomButtons({ market }: { market: ApiTypes.MarketDto }) { const wallet = useAtomValue(walletAtom).pipe(Result.getOrElse(() => null)); if (!isWalletConnected(wallet)) { @@ -101,7 +116,7 @@ function PositionDetailsContent({ marketRef, initialTab, }: { - marketRef: AtomRef.AtomRef; + marketRef: AtomRef.AtomRef; initialTab?: "overview" | "position" | "orders"; }) { const market = useAtomRef(marketRef); @@ -130,32 +145,32 @@ function PositionDetailsContent({
- + {symbol} - - + + {maxLeverage}x - +
- + {formatAmount(market.markPrice)} - - + {isPositive ? "+" : ""} {formatPercentage(market.priceChangePercent24h)} - +
{/* Chart Placeholder */}
- +
{/* Tabs */} @@ -203,12 +218,12 @@ export function PositionDetails() { if (!Result.isSuccess(marketRef)) { return (
-

+ Market not found -

-

+ + The market you're looking for doesn't exist -

+ diff --git a/src/components/modules/PositionDetails/Overview/loading.tsx b/packages/widget/src/components/modules/PositionDetails/Overview/loading.tsx similarity index 92% rename from src/components/modules/PositionDetails/Overview/loading.tsx rename to packages/widget/src/components/modules/PositionDetails/Overview/loading.tsx index 0a776af..a24c8cb 100644 --- a/src/components/modules/PositionDetails/Overview/loading.tsx +++ b/packages/widget/src/components/modules/PositionDetails/Overview/loading.tsx @@ -1,4 +1,4 @@ -import { Skeleton } from "@/components/ui/skeleton"; +import { Skeleton } from "@yieldxyz/perps-common/components"; export function PositionDetailsLoading() { return ( diff --git a/src/components/modules/PositionDetails/Overview/modify-dialog.tsx b/packages/widget/src/components/modules/PositionDetails/Overview/modify-dialog.tsx similarity index 79% rename from src/components/modules/PositionDetails/Overview/modify-dialog.tsx rename to packages/widget/src/components/modules/PositionDetails/Overview/modify-dialog.tsx index 2a3242f..18c8adf 100644 --- a/src/components/modules/PositionDetails/Overview/modify-dialog.tsx +++ b/packages/widget/src/components/modules/PositionDetails/Overview/modify-dialog.tsx @@ -1,8 +1,7 @@ import { Link } from "@tanstack/react-router"; +import { Button, Dialog, Text } from "@yieldxyz/perps-common/components"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; import { ArrowDownRight, ArrowUpRight } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { Dialog } from "@/components/ui/dialog"; -import type { PositionDtoSide } from "@/services/api-client/client-factory"; const MODIFY_OPTIONS = [ { @@ -24,7 +23,7 @@ export function ModifyDialog({ side, }: { marketId: string; - side: PositionDtoSide; + side: ApiTypes.PositionDtoSide; }) { return ( @@ -33,7 +32,8 @@ export function ModifyDialog({ render={(props) => ( @@ -67,12 +67,12 @@ export function ModifyDialog({
- + {option.label} - - + + {option.description} - +
); diff --git a/packages/widget/src/components/modules/PositionDetails/Overview/overview-tab-content.tsx b/packages/widget/src/components/modules/PositionDetails/Overview/overview-tab-content.tsx new file mode 100644 index 0000000..7e3c99c --- /dev/null +++ b/packages/widget/src/components/modules/PositionDetails/Overview/overview-tab-content.tsx @@ -0,0 +1,96 @@ +import { Card, CardSection, Text } from "@yieldxyz/perps-common/components"; +import { + estimateLowHighFromAbsoluteChange, + formatAmount, + formatCompactUsdAmount, + formatRate, +} from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; + +export function OverviewTabContent({ market }: { market: ApiTypes.MarketDto }) { + const currentPrice = market.markPrice; + const priceChange24h = market.priceChange24h; + + const { low: estimatedLow, high: estimatedHigh } = + estimateLowHighFromAbsoluteChange({ + currentPrice, + priceChange24h, + }); + + return ( + + +
+ + 24h low + + + {formatAmount(estimatedLow)} + +
+
+ + 24h high + + + {formatAmount(estimatedHigh)} + +
+
+ +
+ + 24h volume + + + {formatCompactUsdAmount(market.volume24h)} + +
+
+ + Open Interest + + + {formatCompactUsdAmount(market.openInterest)} + +
+
+ +
+ + Funding Rate + + + {formatRate(market.fundingRate, { maximumFractionDigits: 4 })} + +
+
+ + Funding Interval + + + {market.fundingRateIntervalHours}h + +
+
+ +
+ + Maker Fee + + + {market.makerFee ? formatRate(market.makerFee) : "-"} + +
+
+ + Taker Fee + + + {market.takerFee ? formatRate(market.takerFee) : "-"} + +
+
+
+ ); +} diff --git a/src/components/modules/Root/PreloadAtoms.tsx b/packages/widget/src/components/modules/Root/PreloadAtoms.tsx similarity index 70% rename from src/components/modules/Root/PreloadAtoms.tsx rename to packages/widget/src/components/modules/Root/PreloadAtoms.tsx index 49bba95..06dfdca 100644 --- a/src/components/modules/Root/PreloadAtoms.tsx +++ b/packages/widget/src/components/modules/Root/PreloadAtoms.tsx @@ -1,14 +1,18 @@ import { Result, useAtomMount, useAtomValue } from "@effect-atom/atom-react"; -import { marketsAtom, refreshMarketsAtom } from "@/atoms/markets-atoms"; import { + marketsAtom, + moralisTokenBalancesAtom, ordersAtom, positionsAtom, + providersAtom, providersBalancesAtom, -} from "@/atoms/portfolio-atoms"; -import { providersAtom } from "@/atoms/providers-atoms"; -import { moralisTokenBalancesAtom } from "@/atoms/tokens-atoms"; -import { walletAtom } from "@/atoms/wallet-atom"; -import { isWalletConnected, type WalletConnected } from "@/domain/wallet"; + refreshMarketsAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; const PreloadWalletConnectedAtoms = ({ wallet, diff --git a/src/components/molecules/account-value-display.tsx b/packages/widget/src/components/molecules/account-value-display.tsx similarity index 62% rename from src/components/molecules/account-value-display.tsx rename to packages/widget/src/components/molecules/account-value-display.tsx index be6948f..8d2ce7d 100644 --- a/src/components/molecules/account-value-display.tsx +++ b/packages/widget/src/components/molecules/account-value-display.tsx @@ -1,8 +1,8 @@ import { Result, useAtomValue } from "@effect-atom/atom-react"; -import { selectedProviderBalancesAtom } from "@/atoms/portfolio-atoms"; -import { Skeleton } from "@/components/ui/skeleton"; -import type { WalletConnected } from "@/domain/wallet"; -import { cn, formatAmount, getTokenLogo } from "@/lib/utils"; +import { selectedProviderBalancesAtom } from "@yieldxyz/perps-common/atoms"; +import { Skeleton, Text } from "@yieldxyz/perps-common/components"; +import type { WalletConnected } from "@yieldxyz/perps-common/domain"; +import { cn, formatAmount, getTokenLogo } from "@yieldxyz/perps-common/lib"; export function AccountValueDisplay({ wallet }: { wallet: WalletConnected }) { const selectedProviderBalances = useAtomValue( @@ -18,12 +18,12 @@ export function AccountValueDisplay({ wallet }: { wallet: WalletConnected }) { className="size-9" />
-

+ Account value -

-

+ + {formatAmount(selectedProviderBalances.value.accountValue)} -

+
); diff --git a/src/components/molecules/navigation/back-button.tsx b/packages/widget/src/components/molecules/navigation/back-button.tsx similarity index 84% rename from src/components/molecules/navigation/back-button.tsx rename to packages/widget/src/components/molecules/navigation/back-button.tsx index 615dfee..09991ca 100644 --- a/src/components/molecules/navigation/back-button.tsx +++ b/packages/widget/src/components/molecules/navigation/back-button.tsx @@ -1,6 +1,6 @@ import { useRouter } from "@tanstack/react-router"; +import { Button } from "@yieldxyz/perps-common/components"; import { ChevronLeft } from "lucide-react"; -import { Button } from "@/components/ui/button"; export function BackButton() { const { history } = useRouter(); diff --git a/src/components/molecules/navigation/wallet-protected-route.tsx b/packages/widget/src/components/molecules/navigation/wallet-protected-route.tsx similarity index 73% rename from src/components/molecules/navigation/wallet-protected-route.tsx rename to packages/widget/src/components/molecules/navigation/wallet-protected-route.tsx index 43ee7cc..05d5df1 100644 --- a/src/components/molecules/navigation/wallet-protected-route.tsx +++ b/packages/widget/src/components/molecules/navigation/wallet-protected-route.tsx @@ -1,7 +1,10 @@ import { Result, useAtomValue } from "@effect-atom/atom-react"; import { Navigate } from "@tanstack/react-router"; -import { walletAtom } from "@/atoms/wallet-atom"; -import { isWalletConnected, type WalletConnected } from "@/domain/wallet"; +import { walletAtom } from "@yieldxyz/perps-common/atoms"; +import { + isWalletConnected, + type WalletConnected, +} from "@yieldxyz/perps-common/domain"; export const WalletProtectedRoute = ({ children, diff --git a/src/components/molecules/provider-select.tsx b/packages/widget/src/components/molecules/provider-select.tsx similarity index 82% rename from src/components/molecules/provider-select.tsx rename to packages/widget/src/components/molecules/provider-select.tsx index 4266aee..2449ca7 100644 --- a/src/components/molecules/provider-select.tsx +++ b/packages/widget/src/components/molecules/provider-select.tsx @@ -1,4 +1,9 @@ import { Result, useAtomValue } from "@effect-atom/atom-react"; +import hyperliquidLogo from "@yieldxyz/perps-common/assets/hyperliquid.png"; +import { providersAtom } from "@yieldxyz/perps-common/atoms"; +import { Button, Dialog, Text } from "@yieldxyz/perps-common/components"; +import { cn } from "@yieldxyz/perps-common/lib"; +import type { ApiTypes } from "@yieldxyz/perps-common/services"; import { ChevronDown } from "lucide-react"; import { type ComponentProps, @@ -7,20 +12,14 @@ import { useContext, useState, } from "react"; -import hyperliquidLogo from "@/assets/hyperliquid.png"; -import { providersAtom } from "@/atoms/providers-atoms"; -import { Button } from "@/components/ui/button"; -import { Dialog } from "@/components/ui/dialog"; -import { cn } from "@/lib/utils"; -import type { ProviderDto } from "@/services/api-client/api-schemas"; // Context for sharing state between components interface ProviderSelectContextValue { open: boolean; setOpen: (open: boolean) => void; - selectedProvider: ProviderDto | null; - setSelectedProvider: (provider: ProviderDto) => void; - providers: ReadonlyArray; + selectedProvider: ApiTypes.ProviderDto | null; + setSelectedProvider: (provider: ApiTypes.ProviderDto) => void; + providers: ReadonlyArray; emptyContent: ReactNode; } @@ -42,7 +41,9 @@ function useProviderSelect() { function DefaultEmptyContent() { return (
- No providers available + + No providers available +
); } @@ -50,10 +51,10 @@ function DefaultEmptyContent() { // Root component - handles state interface RootProps { children: ReactNode; - defaultProvider?: ProviderDto; - value?: ProviderDto; - onValueChange?: (provider: ProviderDto) => void; - providers: ReadonlyArray; + defaultProvider?: ApiTypes.ProviderDto; + value?: ApiTypes.ProviderDto; + onValueChange?: (provider: ApiTypes.ProviderDto) => void; + providers: ReadonlyArray; emptyContent?: ReactNode; } @@ -66,12 +67,13 @@ function Root({ emptyContent = , }: RootProps) { const [open, setOpen] = useState(false); - const [internalProvider, setInternalProvider] = useState( - defaultProvider ?? providers[0] ?? null, - ); + const [internalProvider, setInternalProvider] = + useState( + defaultProvider ?? providers[0] ?? null, + ); const selectedProvider = value ?? internalProvider; - const setSelectedProvider = (provider: ProviderDto) => { + const setSelectedProvider = (provider: ApiTypes.ProviderDto) => { if (!value) { setInternalProvider(provider); } @@ -120,16 +122,16 @@ function Trigger({ className, children, ...props }: TriggerProps) { alt={selectedProvider.name} className="size-6 rounded-full" /> - + {selectedProvider.name} - + ) : ( <> - + Select provider - + ))} @@ -231,9 +233,9 @@ function List({ className, children, ...props }: ListProps) { // Item component - individual provider item interface ItemProps extends Omit, "onSelect"> { - provider: ProviderDto; + provider: ApiTypes.ProviderDto; selected?: boolean; - onSelect?: (provider: ProviderDto) => void; + onSelect?: (provider: ApiTypes.ProviderDto) => void; } function Item({ @@ -277,9 +279,9 @@ function Item({ className="size-[26px] rounded-full object-cover" />
- + {provider.name} - +
); diff --git a/src/components/molecules/sign/index.tsx b/packages/widget/src/components/molecules/sign/index.tsx similarity index 67% rename from src/components/molecules/sign/index.tsx rename to packages/widget/src/components/molecules/sign/index.tsx index 84a5820..c440b50 100644 --- a/src/components/molecules/sign/index.tsx +++ b/packages/widget/src/components/molecules/sign/index.tsx @@ -1,10 +1,10 @@ import { useNavigate } from "@tanstack/react-router"; -import { signActionAtoms } from "@/atoms/actions-atoms"; -import { BackButton } from "@/components/molecules/navigation/back-button"; -import { WalletProtectedRoute } from "@/components/molecules/navigation/wallet-protected-route"; -import { SignTransactions } from "@/components/molecules/sign/sign-content"; -import { Button } from "@/components/ui/button"; -import type { WalletConnected } from "@/domain/wallet"; +import { signActionAtoms } from "@yieldxyz/perps-common/atoms"; +import { Button, Text } from "@yieldxyz/perps-common/components"; +import type { WalletConnected } from "@yieldxyz/perps-common/domain"; +import { BackButton } from "../navigation/back-button"; +import { WalletProtectedRoute } from "../navigation/wallet-protected-route"; +import { SignTransactions } from "./sign-content"; function SignTransactionsWithWallet({ wallet, @@ -22,9 +22,9 @@ function SignTransactionsWithWallet({ {/* Header */}
-

+ {title} -

+
{/* Sign Transactions Component */} diff --git a/packages/widget/src/components/molecules/sign/sign-content.tsx b/packages/widget/src/components/molecules/sign/sign-content.tsx new file mode 100644 index 0000000..386bdfd --- /dev/null +++ b/packages/widget/src/components/molecules/sign/sign-content.tsx @@ -0,0 +1,57 @@ +import { Result, useAtomSet, useAtomValue } from "@effect-atom/atom-react"; +import { Navigate } from "@tanstack/react-router"; +import type { signActionAtoms } from "@yieldxyz/perps-common/atoms"; +import { + Skeleton, + TransactionProgress, +} from "@yieldxyz/perps-common/components"; +import type { SignTransactionsState } from "@yieldxyz/perps-common/domain"; + +interface SignTransactionsProps { + state: SignTransactionsState; + retry: () => void; +} + +function SignTransactionsWithState({ state, retry }: SignTransactionsProps) { + return ( +
+

+ Progress +

+ + retry()} /> +
+ ); +} + +export function SignTransactions({ + machineAtoms, +}: { + machineAtoms: ReturnType; +}) { + const { machineStreamAtom, retryMachineAtom } = machineAtoms; + const state = useAtomValue(machineStreamAtom); + const retry = useAtomSet(retryMachineAtom); + + const result = Result.all({ state, retry }); + + if (Result.isFailure(result)) { + return ( + <> + + + + ); + } + + if (Result.isSuccess(result)) { + return ( + + ); + } + + return ; +} diff --git a/src/components/molecules/token-balances-select.tsx b/packages/widget/src/components/molecules/token-balances-select.tsx similarity index 91% rename from src/components/molecules/token-balances-select.tsx rename to packages/widget/src/components/molecules/token-balances-select.tsx index 9bd4fb0..16cb074 100644 --- a/src/components/molecules/token-balances-select.tsx +++ b/packages/widget/src/components/molecules/token-balances-select.tsx @@ -1,32 +1,36 @@ import { EvmNetworks } from "@stakekit/common"; -import { Record } from "effect"; -import { ChevronDown } from "lucide-react"; -import { - type ComponentProps, - createContext, - type ReactNode, - useContext, - useState, -} from "react"; import { type TokenBalances, yieldApiNetworkToMoralisChain, -} from "@/atoms/tokens-atoms"; +} from "@yieldxyz/perps-common/atoms"; import { + Button, + Dialog, + Tabs, + TabsContent, + TabsList, + TabsTrigger, + Text, TokenIcon, type TokenIconProps, -} from "@/components/molecules/token-icon"; -import { Button } from "@/components/ui/button"; -import { Dialog } from "@/components/ui/dialog"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import type { TokenBalance } from "@/domain/types"; +} from "@yieldxyz/perps-common/components"; +import type { TokenBalance } from "@yieldxyz/perps-common/domain"; import { cn, formatSnakeCase, formatTokenAmount, getNetworkLogo, getTokenString, -} from "@/lib/utils"; +} from "@yieldxyz/perps-common/lib"; +import { Record } from "effect"; +import { ChevronDown } from "lucide-react"; +import { + type ComponentProps, + createContext, + type ReactNode, + useContext, + useState, +} from "react"; interface TokenBalanceSelectContextValue { open: boolean; @@ -54,7 +58,9 @@ function useTokenBalanceSelect() { function DefaultEmptyContent() { return (
- No tokens available + + No tokens available +
); } @@ -125,7 +131,7 @@ function TokenBalanceLogo({ )} @@ -154,16 +160,16 @@ function Trigger({ className, children, ...props }: TriggerProps) { (selectedTokenBalance ? ( <> - + {selectedTokenBalance.token.symbol} - + ) : ( <> - + Select token - + ))} @@ -184,7 +190,9 @@ function NetworkTabTrigger({ network }: NetworkTabTriggerProps) { name={formatSnakeCase(network)} size="xs" /> - {formatSnakeCase(network)} + + {formatSnakeCase(network)} + ); } @@ -405,22 +413,22 @@ function Item({ >
- + {tokenBalance.token.symbol} - - + + {tokenBalance.token.name} •{" "} {formatSnakeCase(tokenBalance.token.network)} - +
{tokenBalance.amount && ( - + {formatTokenAmount({ amount: tokenBalance.amount, symbol: tokenBalance.token.symbol, })} - + )}
diff --git a/packages/widget/src/main.css b/packages/widget/src/main.css new file mode 100644 index 0000000..7bdd4b1 --- /dev/null +++ b/packages/widget/src/main.css @@ -0,0 +1,19 @@ +html { + background: var(--surface-1); +} + +#app { + min-height: 100vh; + padding: 16px; + display: flex; + justify-content: center; + align-items: flex-start; + padding-top: 60px; + background: var(--surface-1); +} + +@media (min-width: 768px) { + #app { + padding: 32px; + } +} diff --git a/packages/widget/src/main.tsx b/packages/widget/src/main.tsx new file mode 100644 index 0000000..ba90f6b --- /dev/null +++ b/packages/widget/src/main.tsx @@ -0,0 +1,11 @@ +import "./main.css"; +import ReactDOM from "react-dom/client"; +import { Widget } from "./app"; + +import "@yieldxyz/perps-common/styles"; + +const rootElement = document.getElementById("app"); +if (rootElement && !rootElement.innerHTML) { + const root = ReactDOM.createRoot(rootElement); + root.render(); +} diff --git a/src/routeTree.gen.ts b/packages/widget/src/routeTree.gen.ts similarity index 100% rename from src/routeTree.gen.ts rename to packages/widget/src/routeTree.gen.ts diff --git a/src/routes/__root.tsx b/packages/widget/src/routes/__root.tsx similarity index 100% rename from src/routes/__root.tsx rename to packages/widget/src/routes/__root.tsx diff --git a/src/routes/account/deposit.tsx b/packages/widget/src/routes/account/deposit.tsx similarity index 66% rename from src/routes/account/deposit.tsx rename to packages/widget/src/routes/account/deposit.tsx index eb98155..4514716 100644 --- a/src/routes/account/deposit.tsx +++ b/packages/widget/src/routes/account/deposit.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { AccountDeposit } from "@/components/modules/Account/Deposit"; +import { AccountDeposit } from "../../components/modules/Account/Deposit"; export const Route = createFileRoute("/account/deposit")({ component: AccountDeposit, diff --git a/src/routes/account/deposit_/sign.tsx b/packages/widget/src/routes/account/deposit_/sign.tsx similarity index 65% rename from src/routes/account/deposit_/sign.tsx rename to packages/widget/src/routes/account/deposit_/sign.tsx index e6840f7..7f4b74d 100644 --- a/src/routes/account/deposit_/sign.tsx +++ b/packages/widget/src/routes/account/deposit_/sign.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { DepositSignRoute } from "@/components/modules/Account/Deposit/sign"; +import { DepositSignRoute } from "../../../components/modules/Account/Deposit/sign"; export const Route = createFileRoute("/account/deposit_/sign")({ component: DepositSignRoute, diff --git a/src/routes/account/index.tsx b/packages/widget/src/routes/account/index.tsx similarity index 65% rename from src/routes/account/index.tsx rename to packages/widget/src/routes/account/index.tsx index 31145b0..4e0518e 100644 --- a/src/routes/account/index.tsx +++ b/packages/widget/src/routes/account/index.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { AccountBalances } from "@/components/modules/Account/balance"; +import { AccountBalances } from "../../components/modules/Account/balance"; export const Route = createFileRoute("/account/")({ component: AccountBalances, diff --git a/src/routes/account/withdraw.tsx b/packages/widget/src/routes/account/withdraw.tsx similarity index 65% rename from src/routes/account/withdraw.tsx rename to packages/widget/src/routes/account/withdraw.tsx index 5b0112c..09b1f8d 100644 --- a/src/routes/account/withdraw.tsx +++ b/packages/widget/src/routes/account/withdraw.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { AccountWithdraw } from "@/components/modules/Account/Withdraw/index"; +import { AccountWithdraw } from "../../components/modules/Account/Withdraw"; export const Route = createFileRoute("/account/withdraw")({ component: AccountWithdraw, diff --git a/src/routes/account/withdraw_/sign.tsx b/packages/widget/src/routes/account/withdraw_/sign.tsx similarity index 64% rename from src/routes/account/withdraw_/sign.tsx rename to packages/widget/src/routes/account/withdraw_/sign.tsx index bbb56e8..c3f260d 100644 --- a/src/routes/account/withdraw_/sign.tsx +++ b/packages/widget/src/routes/account/withdraw_/sign.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { WithdrawSignRoute } from "@/components/modules/Account/Withdraw/sign"; +import { WithdrawSignRoute } from "../../../components/modules/Account/Withdraw/sign"; export const Route = createFileRoute("/account/withdraw_/sign")({ component: WithdrawSignRoute, diff --git a/src/routes/index.tsx b/packages/widget/src/routes/index.tsx similarity index 71% rename from src/routes/index.tsx rename to packages/widget/src/routes/index.tsx index 079f498..a1797e0 100644 --- a/src/routes/index.tsx +++ b/packages/widget/src/routes/index.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { Home } from "@/components/modules/Home"; +import { Home } from "../components/modules/Home"; export const Route = createFileRoute("/")({ component: Home, diff --git a/src/routes/order/$marketId/$side/increase_/index.tsx b/packages/widget/src/routes/order/$marketId/$side/increase_/index.tsx similarity index 85% rename from src/routes/order/$marketId/$side/increase_/index.tsx rename to packages/widget/src/routes/order/$marketId/$side/increase_/index.tsx index 6990c72..9dda263 100644 --- a/src/routes/order/$marketId/$side/increase_/index.tsx +++ b/packages/widget/src/routes/order/$marketId/$side/increase_/index.tsx @@ -1,5 +1,5 @@ import { createFileRoute, getRouteApi, Navigate } from "@tanstack/react-router"; -import { MarketOrder } from "@/components/modules/Order/Overview"; +import { MarketOrder } from "../../../../../components/modules/Order/Overview"; export const Route = createFileRoute("/order/$marketId/$side/increase_/")({ component: RouteComponent, diff --git a/src/routes/order/$marketId/$side/index.tsx b/packages/widget/src/routes/order/$marketId/$side/index.tsx similarity index 85% rename from src/routes/order/$marketId/$side/index.tsx rename to packages/widget/src/routes/order/$marketId/$side/index.tsx index f50e724..ac45309 100644 --- a/src/routes/order/$marketId/$side/index.tsx +++ b/packages/widget/src/routes/order/$marketId/$side/index.tsx @@ -1,5 +1,5 @@ import { createFileRoute, getRouteApi, Navigate } from "@tanstack/react-router"; -import { MarketOrder } from "@/components/modules/Order/Overview"; +import { MarketOrder } from "../../../../components/modules/Order/Overview"; export const Route = createFileRoute("/order/$marketId/$side/")({ component: RouteComponent, diff --git a/src/routes/order/$marketId/$side/sign.tsx b/packages/widget/src/routes/order/$marketId/$side/sign.tsx similarity index 68% rename from src/routes/order/$marketId/$side/sign.tsx rename to packages/widget/src/routes/order/$marketId/$side/sign.tsx index 29eb7e4..061019e 100644 --- a/src/routes/order/$marketId/$side/sign.tsx +++ b/packages/widget/src/routes/order/$marketId/$side/sign.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { OrderSignRoute } from "@/components/modules/Order/sign"; +import { OrderSignRoute } from "../../../../components/modules/Order/sign"; export const Route = createFileRoute("/order/$marketId/$side/sign")({ component: OrderSignRoute, diff --git a/src/routes/position-details/$marketId/cancel-order_/sign.tsx b/packages/widget/src/routes/position-details/$marketId/cancel-order_/sign.tsx similarity index 62% rename from src/routes/position-details/$marketId/cancel-order_/sign.tsx rename to packages/widget/src/routes/position-details/$marketId/cancel-order_/sign.tsx index 0d53bc2..1f2ca50 100644 --- a/src/routes/position-details/$marketId/cancel-order_/sign.tsx +++ b/packages/widget/src/routes/position-details/$marketId/cancel-order_/sign.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { CancelOrderSignRoute } from "@/components/modules/PositionDetails/Overview/CancelOrder/sign"; +import { CancelOrderSignRoute } from "../../../../components/modules/PositionDetails/Overview/CancelOrder/sign"; export const Route = createFileRoute( "/position-details/$marketId/cancel-order_/sign", diff --git a/src/routes/position-details/$marketId/close.tsx b/packages/widget/src/routes/position-details/$marketId/close.tsx similarity index 66% rename from src/routes/position-details/$marketId/close.tsx rename to packages/widget/src/routes/position-details/$marketId/close.tsx index 86cfcff..5911589 100644 --- a/src/routes/position-details/$marketId/close.tsx +++ b/packages/widget/src/routes/position-details/$marketId/close.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { ClosePositionRoute } from "@/components/modules/PositionDetails/Close"; +import { ClosePositionRoute } from "../../../components/modules/PositionDetails/Close"; export const Route = createFileRoute("/position-details/$marketId/close")({ component: ClosePositionRoute, diff --git a/src/routes/position-details/$marketId/close_/sign.tsx b/packages/widget/src/routes/position-details/$marketId/close_/sign.tsx similarity index 66% rename from src/routes/position-details/$marketId/close_/sign.tsx rename to packages/widget/src/routes/position-details/$marketId/close_/sign.tsx index 76f9de8..1aa3400 100644 --- a/src/routes/position-details/$marketId/close_/sign.tsx +++ b/packages/widget/src/routes/position-details/$marketId/close_/sign.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { CloseSignRoute } from "@/components/modules/PositionDetails/Close/sign"; +import { CloseSignRoute } from "../../../../components/modules/PositionDetails/Close/sign"; export const Route = createFileRoute("/position-details/$marketId/close_/sign")( { diff --git a/src/routes/position-details/$marketId/edit-leverage_/index.tsx b/packages/widget/src/routes/position-details/$marketId/edit-leverage_/index.tsx similarity index 62% rename from src/routes/position-details/$marketId/edit-leverage_/index.tsx rename to packages/widget/src/routes/position-details/$marketId/edit-leverage_/index.tsx index 5aab6ed..ded07ed 100644 --- a/src/routes/position-details/$marketId/edit-leverage_/index.tsx +++ b/packages/widget/src/routes/position-details/$marketId/edit-leverage_/index.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { EditLeverageSignRoute } from "@/components/modules/PositionDetails/Overview/EditLeverage/sign"; +import { EditLeverageSignRoute } from "../../../../components/modules/PositionDetails/Overview/EditLeverage/sign"; export const Route = createFileRoute( "/position-details/$marketId/edit-leverage_/", diff --git a/src/routes/position-details/$marketId/edit-sl-tp_/sign.tsx b/packages/widget/src/routes/position-details/$marketId/edit-sl-tp_/sign.tsx similarity index 63% rename from src/routes/position-details/$marketId/edit-sl-tp_/sign.tsx rename to packages/widget/src/routes/position-details/$marketId/edit-sl-tp_/sign.tsx index 203bcbf..2b5a1fe 100644 --- a/src/routes/position-details/$marketId/edit-sl-tp_/sign.tsx +++ b/packages/widget/src/routes/position-details/$marketId/edit-sl-tp_/sign.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { EditSLTPSignRoute } from "@/components/modules/PositionDetails/Overview/Position/sign"; +import { EditSLTPSignRoute } from "../../../../components/modules/PositionDetails/Overview/Position/sign"; export const Route = createFileRoute( "/position-details/$marketId/edit-sl-tp_/sign", diff --git a/src/routes/position-details/$marketId/index.tsx b/packages/widget/src/routes/position-details/$marketId/index.tsx similarity index 80% rename from src/routes/position-details/$marketId/index.tsx rename to packages/widget/src/routes/position-details/$marketId/index.tsx index bfc27bd..a92a4e6 100644 --- a/src/routes/position-details/$marketId/index.tsx +++ b/packages/widget/src/routes/position-details/$marketId/index.tsx @@ -1,6 +1,6 @@ import { createFileRoute } from "@tanstack/react-router"; import { Schema } from "effect"; -import PositionDetails from "@/components/modules/PositionDetails/Overview"; +import PositionDetails from "../../../components/modules/PositionDetails/Overview"; export const Route = createFileRoute("/position-details/$marketId/")({ component: PositionDetails, diff --git a/packages/widget/src/styles.css b/packages/widget/src/styles.css new file mode 100644 index 0000000..58f9b3d --- /dev/null +++ b/packages/widget/src/styles.css @@ -0,0 +1,2 @@ +@import "@yieldxyz/perps-common/styles"; +@source "../../common/src"; diff --git a/packages/widget/tsconfig.json b/packages/widget/tsconfig.json new file mode 100644 index 0000000..6578f19 --- /dev/null +++ b/packages/widget/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "noEmit": false, + "outDir": "./dist", + "rootDir": "." + }, + "references": [{ "path": "../common" }], + "include": ["src"] +} diff --git a/packages/widget/vite.config.ts b/packages/widget/vite.config.ts new file mode 100644 index 0000000..c02ea85 --- /dev/null +++ b/packages/widget/vite.config.ts @@ -0,0 +1,4 @@ +import { commonViteConfig } from "@yieldxyz/perps-common/vite.config"; +import { defineConfig } from "vite"; + +export default defineConfig(commonViteConfig); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf4f414..52c2047 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,171 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@base-ui/react': + specifier: ^1.1.0 + version: 1.1.0 + '@biomejs/biome': + specifier: ^2.3.14 + version: 2.3.14 + '@commitlint/cli': + specifier: ^20.4.1 + version: 20.4.1 + '@commitlint/config-conventional': + specifier: ^20.4.1 + version: 20.4.1 + '@effect-atom/atom-react': + specifier: ^0.5.0 + version: 0.5.0 + '@effect/language-service': + specifier: ^0.73.0 + version: 0.73.0 + '@effect/platform': + specifier: ^0.94.3 + version: 0.94.3 + '@effect/platform-node': + specifier: ^0.104.1 + version: 0.104.1 + '@ledgerhq/wallet-api-client': + specifier: ^1.12.6 + version: 1.12.6 + '@lucas-barake/effect-form-react': + specifier: ^0.18.0 + version: 0.18.0 + '@reown/appkit': + specifier: ^1.8.17 + version: 1.8.17 + '@reown/appkit-adapter-wagmi': + specifier: ^1.8.17 + version: 1.8.17 + '@stakekit/common': + specifier: ^0.0.61 + version: 0.0.61 + '@tailwindcss/vite': + specifier: ^4.0.6 + version: 4.1.18 + '@tanstack/devtools-vite': + specifier: ^0.5.0 + version: 0.5.0 + '@tanstack/react-devtools': + specifier: ^0.9.4 + version: 0.9.4 + '@tanstack/react-query': + specifier: ^5.90.20 + version: 5.90.20 + '@tanstack/react-router': + specifier: ^1.158.0 + version: 1.158.0 + '@tanstack/react-router-devtools': + specifier: ^1.158.0 + version: 1.158.0 + '@tanstack/react-virtual': + specifier: ^3.13.18 + version: 3.13.18 + '@tanstack/router-cli': + specifier: ^1.158.0 + version: 1.158.0 + '@tanstack/router-plugin': + specifier: ^1.157.8 + version: 1.158.0 + '@testing-library/dom': + specifier: ^10.4.0 + version: 10.4.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.2 + '@tim-smart/openapi-gen': + specifier: ^0.4.13 + version: 0.4.13 + '@types/node': + specifier: ^25.2.0 + version: 25.2.0 + '@types/react': + specifier: ^19.2.11 + version: 19.2.11 + '@types/react-dom': + specifier: ^19.2.0 + version: 19.2.3 + '@vite-pwa/assets-generator': + specifier: ^1.0.2 + version: 1.0.2 + '@vitejs/plugin-react': + specifier: ^5.1.3 + version: 5.1.3 + '@vitest/browser-playwright': + specifier: ^4.0.18 + version: 4.0.18 + babel-plugin-react-compiler: + specifier: ^1.0.0 + version: 1.0.0 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + effect: + specifier: ^3.19.16 + version: 3.19.16 + husky: + specifier: ^9.1.7 + version: 9.1.7 + jsdom: + specifier: ^28.0.0 + version: 28.0.0 + lucide-react: + specifier: ^0.563.0 + version: 0.563.0 + openapi-filter: + specifier: ^3.2.3 + version: 3.2.3 + react: + specifier: ^19.2.0 + version: 19.2.4 + react-dom: + specifier: ^19.2.0 + version: 19.2.4 + sonner: + specifier: ^2.0.7 + version: 2.0.7 + tailwind-merge: + specifier: ^3.0.2 + version: 3.4.0 + tailwindcss: + specifier: ^4.0.6 + version: 4.1.18 + tsx: + specifier: ^4.21.0 + version: 4.21.0 + turbo: + specifier: ^2.8.3 + version: 2.8.3 + tw-animate-css: + specifier: ^1.3.6 + version: 1.4.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + viem: + specifier: ^2.45.0 + version: 2.45.1 + vite: + specifier: ^7.3.1 + version: 7.3.1 + vite-plugin-node-polyfills: + specifier: ^0.25.0 + version: 0.25.0 + vitest: + specifier: ^4.0.18 + version: 4.0.18 + vitest-browser-react: + specifier: ^2.0.4 + version: 2.0.5 + wagmi: + specifier: ^3.4.2 + version: 3.4.2 + patchedDependencies: '@tim-smart/openapi-gen': hash: 36720013d0f70c201ce8f233e38a7b93fc9fce74a17f9bc4293c3cf891b4fdc6 @@ -12,166 +177,486 @@ patchedDependencies: importers: .: + devDependencies: + '@biomejs/biome': + specifier: 'catalog:' + version: 2.3.14 + '@commitlint/cli': + specifier: 'catalog:' + version: 20.4.1(@types/node@25.2.0)(typescript@5.9.3) + '@commitlint/config-conventional': + specifier: 'catalog:' + version: 20.4.1 + '@effect/language-service': + specifier: 'catalog:' + version: 0.73.0 + husky: + specifier: 'catalog:' + version: 9.1.7 + turbo: + specifier: 'catalog:' + version: 2.8.3 + + packages/common: + dependencies: + '@base-ui/react': + specifier: 'catalog:' + version: 1.1.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@effect-atom/atom-react': + specifier: 'catalog:' + version: 0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)(scheduler@0.27.0) + '@effect/experimental': + specifier: ^0.58.0 + version: 0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + '@effect/platform': + specifier: 'catalog:' + version: 0.94.3(effect@3.19.16) + '@effect/platform-node': + specifier: 'catalog:' + version: 0.104.1(@effect/cluster@0.56.1(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(bufferutil@4.1.0)(effect@3.19.16)(utf-8-validate@5.0.10) + '@ledgerhq/wallet-api-client': + specifier: 'catalog:' + version: 1.12.6(@ton/crypto@3.3.0) + '@lucas-barake/effect-form-react': + specifier: 'catalog:' + version: 0.18.0(@effect-atom/atom-react@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)(scheduler@0.27.0))(@effect-atom/atom@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4) + '@reown/appkit': + specifier: 'catalog:' + version: 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-adapter-wagmi': + specifier: 'catalog:' + version: 1.8.17(0a70dc20fe0c1a07d4cc186a53526855) + '@stakekit/common': + specifier: 'catalog:' + version: 0.0.61 + '@tailwindcss/vite': + specifier: 'catalog:' + version: 4.1.18(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/react-devtools': + specifier: 'catalog:' + version: 0.9.4(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(bufferutil@4.1.0)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(solid-js@1.9.11)(utf-8-validate@5.0.10) + '@tanstack/react-query': + specifier: 'catalog:' + version: 5.90.20(react@19.2.4) + '@tanstack/react-router': + specifier: 'catalog:' + version: 1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-router-devtools': + specifier: 'catalog:' + version: 1.158.0(@tanstack/react-router@1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.158.0)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-virtual': + specifier: 'catalog:' + version: 3.13.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + class-variance-authority: + specifier: 'catalog:' + version: 0.7.1 + clsx: + specifier: 'catalog:' + version: 2.1.1 + effect: + specifier: 'catalog:' + version: 3.19.16 + lucide-react: + specifier: 'catalog:' + version: 0.563.0(react@19.2.4) + react: + specifier: 'catalog:' + version: 19.2.4 + react-dom: + specifier: 'catalog:' + version: 19.2.4(react@19.2.4) + sonner: + specifier: 'catalog:' + version: 2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + tailwind-merge: + specifier: 'catalog:' + version: 3.4.0 + tailwindcss: + specifier: 'catalog:' + version: 4.1.18 + tw-animate-css: + specifier: 'catalog:' + version: 1.4.0 + viem: + specifier: 'catalog:' + version: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: + specifier: 'catalog:' + version: 3.4.2(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.4))(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + devDependencies: + '@tanstack/devtools-vite': + specifier: 'catalog:' + version: 0.5.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/router-cli': + specifier: 'catalog:' + version: 1.158.0 + '@testing-library/dom': + specifier: 'catalog:' + version: 10.4.1 + '@testing-library/react': + specifier: 'catalog:' + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tim-smart/openapi-gen': + specifier: 'catalog:' + version: 0.4.13(patch_hash=36720013d0f70c201ce8f233e38a7b93fc9fce74a17f9bc4293c3cf891b4fdc6) + '@types/node': + specifier: 'catalog:' + version: 25.2.0 + '@types/react': + specifier: 'catalog:' + version: 19.2.11 + '@types/react-dom': + specifier: 'catalog:' + version: 19.2.3(@types/react@19.2.11) + '@vite-pwa/assets-generator': + specifier: 'catalog:' + version: 1.0.2 + '@vitejs/plugin-react': + specifier: 'catalog:' + version: 5.1.3(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/browser-playwright': + specifier: 'catalog:' + version: 4.0.18(bufferutil@4.1.0)(playwright@1.58.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) + babel-plugin-react-compiler: + specifier: 'catalog:' + version: 1.0.0 + jsdom: + specifier: 'catalog:' + version: 28.0.0(@noble/hashes@1.8.0) + openapi-filter: + specifier: 'catalog:' + version: 3.2.3 + tsx: + specifier: 'catalog:' + version: 4.21.0 + typescript: + specifier: 'catalog:' + version: 5.9.3 + vite: + specifier: 'catalog:' + version: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite-plugin-node-polyfills: + specifier: 'catalog:' + version: 0.25.0(rollup@4.57.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + vitest: + specifier: 'catalog:' + version: 4.0.18(@types/node@25.2.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vitest-browser-react: + specifier: 'catalog:' + version: 2.0.5(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18) + + packages/dashboard: dependencies: '@base-ui/react': - specifier: ^1.1.0 - version: 1.1.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 'catalog:' + version: 1.1.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@effect-atom/atom-react': - specifier: ^0.4.6 - version: 0.4.6(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)(react@19.2.3)(scheduler@0.27.0) + specifier: 'catalog:' + version: 0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)(scheduler@0.27.0) '@effect/experimental': specifier: ^0.58.0 - version: 0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) + version: 0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) '@effect/platform': - specifier: ^0.94.2 - version: 0.94.2(effect@3.19.15) + specifier: 'catalog:' + version: 0.94.3(effect@3.19.16) '@effect/platform-node': - specifier: ^0.104.1 - version: 0.104.1(@effect/cluster@0.56.1(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(bufferutil@4.1.0)(effect@3.19.15)(utf-8-validate@5.0.10) + specifier: 'catalog:' + version: 0.104.1(@effect/cluster@0.56.1(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(bufferutil@4.1.0)(effect@3.19.16)(utf-8-validate@5.0.10) '@ledgerhq/wallet-api-client': - specifier: ^1.12.6 + specifier: 'catalog:' version: 1.12.6(@ton/crypto@3.3.0) '@lucas-barake/effect-form-react': - specifier: ^0.14.0 - version: 0.14.0(@effect-atom/atom-react@0.4.6(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)(react@19.2.3)(scheduler@0.27.0))(@effect-atom/atom@0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)(react@19.2.3) + specifier: 'catalog:' + version: 0.18.0(@effect-atom/atom-react@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)(scheduler@0.27.0))(@effect-atom/atom@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4) '@reown/appkit': - specifier: ^1.8.17 - version: 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76) + specifier: 'catalog:' + version: 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-adapter-wagmi': - specifier: ^1.8.17 - version: 1.8.17(ddefe6db690784ffc0b4049c81f77f80) + specifier: 'catalog:' + version: 1.8.17(588bfcce98e97548479faa9aa8a7b8c9) '@stakekit/common': - specifier: ^0.0.61 + specifier: 'catalog:' version: 0.0.61 '@tailwindcss/vite': - specifier: ^4.0.6 - version: 4.1.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + specifier: 'catalog:' + version: 4.1.18(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@tanstack/react-devtools': - specifier: ^0.9.2 - version: 0.9.2(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(bufferutil@4.1.0)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.10)(utf-8-validate@5.0.10) + specifier: 'catalog:' + version: 0.9.4(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(bufferutil@4.1.0)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(solid-js@1.9.11)(utf-8-validate@5.0.10) '@tanstack/react-query': - specifier: ^5.90.20 - version: 5.90.20(react@19.2.3) + specifier: 'catalog:' + version: 5.90.20(react@19.2.4) '@tanstack/react-router': - specifier: ^1.157.8 - version: 1.157.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 'catalog:' + version: 1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/react-router-devtools': - specifier: ^1.157.8 - version: 1.157.8(@tanstack/react-router@1.157.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.157.15)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 'catalog:' + version: 1.158.0(@tanstack/react-router@1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.158.0)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/react-virtual': - specifier: ^3.13.18 - version: 3.13.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 'catalog:' + version: 3.13.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@yieldxyz/perps-common': + specifier: workspace:* + version: link:../common + class-variance-authority: + specifier: 'catalog:' + version: 0.7.1 + clsx: + specifier: 'catalog:' + version: 2.1.1 + effect: + specifier: 'catalog:' + version: 3.19.16 + lucide-react: + specifier: 'catalog:' + version: 0.563.0(react@19.2.4) + react: + specifier: 'catalog:' + version: 19.2.4 + react-dom: + specifier: 'catalog:' + version: 19.2.4(react@19.2.4) + sonner: + specifier: 'catalog:' + version: 2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + tailwind-merge: + specifier: 'catalog:' + version: 3.4.0 + tailwindcss: + specifier: 'catalog:' + version: 4.1.18 + tw-animate-css: + specifier: 'catalog:' + version: 1.4.0 + viem: + specifier: 'catalog:' + version: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: + specifier: 'catalog:' + version: 3.4.2(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.4))(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + devDependencies: + '@tanstack/devtools-vite': + specifier: 'catalog:' + version: 0.5.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/router-cli': + specifier: 'catalog:' + version: 1.158.0 '@tanstack/router-plugin': - specifier: ^1.157.8 - version: 1.157.8(@tanstack/react-router@1.157.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + specifier: 'catalog:' + version: 1.158.0(@tanstack/react-router@1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@testing-library/dom': + specifier: 'catalog:' + version: 10.4.1 + '@testing-library/react': + specifier: 'catalog:' + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tim-smart/openapi-gen': + specifier: 'catalog:' + version: 0.4.13(patch_hash=36720013d0f70c201ce8f233e38a7b93fc9fce74a17f9bc4293c3cf891b4fdc6) + '@types/node': + specifier: 'catalog:' + version: 25.2.0 + '@types/react': + specifier: 'catalog:' + version: 19.2.11 + '@types/react-dom': + specifier: 'catalog:' + version: 19.2.3(@types/react@19.2.11) + '@vite-pwa/assets-generator': + specifier: 'catalog:' + version: 1.0.2 + '@vitejs/plugin-react': + specifier: 'catalog:' + version: 5.1.3(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/browser-playwright': + specifier: 'catalog:' + version: 4.0.18(bufferutil@4.1.0)(playwright@1.58.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) + babel-plugin-react-compiler: + specifier: 'catalog:' + version: 1.0.0 + jsdom: + specifier: 'catalog:' + version: 28.0.0(@noble/hashes@1.8.0) + openapi-filter: + specifier: 'catalog:' + version: 3.2.3 + tsx: + specifier: 'catalog:' + version: 4.21.0 + typescript: + specifier: 'catalog:' + version: 5.9.3 + vite: + specifier: 'catalog:' + version: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite-plugin-node-polyfills: + specifier: 'catalog:' + version: 0.25.0(rollup@4.57.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + vitest: + specifier: 'catalog:' + version: 4.0.18(@types/node@25.2.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vitest-browser-react: + specifier: 'catalog:' + version: 2.0.5(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18) + + packages/widget: + dependencies: + '@base-ui/react': + specifier: 'catalog:' + version: 1.1.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@effect-atom/atom-react': + specifier: 'catalog:' + version: 0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)(scheduler@0.27.0) + '@effect/experimental': + specifier: ^0.58.0 + version: 0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + '@effect/platform': + specifier: 'catalog:' + version: 0.94.3(effect@3.19.16) + '@effect/platform-node': + specifier: 'catalog:' + version: 0.104.1(@effect/cluster@0.56.1(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(bufferutil@4.1.0)(effect@3.19.16)(utf-8-validate@5.0.10) + '@ledgerhq/wallet-api-client': + specifier: 'catalog:' + version: 1.12.6(@ton/crypto@3.3.0) + '@lucas-barake/effect-form-react': + specifier: 'catalog:' + version: 0.18.0(@effect-atom/atom-react@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)(scheduler@0.27.0))(@effect-atom/atom@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4) + '@reown/appkit': + specifier: 'catalog:' + version: 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-adapter-wagmi': + specifier: 'catalog:' + version: 1.8.17(588bfcce98e97548479faa9aa8a7b8c9) + '@stakekit/common': + specifier: 'catalog:' + version: 0.0.61 + '@tailwindcss/vite': + specifier: 'catalog:' + version: 4.1.18(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/react-devtools': + specifier: 'catalog:' + version: 0.9.4(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(bufferutil@4.1.0)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(solid-js@1.9.11)(utf-8-validate@5.0.10) + '@tanstack/react-query': + specifier: 'catalog:' + version: 5.90.20(react@19.2.4) + '@tanstack/react-router': + specifier: 'catalog:' + version: 1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-router-devtools': + specifier: 'catalog:' + version: 1.158.0(@tanstack/react-router@1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.158.0)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-virtual': + specifier: 'catalog:' + version: 3.13.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@yieldxyz/perps-common': + specifier: workspace:* + version: link:../common class-variance-authority: - specifier: ^0.7.1 + specifier: 'catalog:' version: 0.7.1 clsx: - specifier: ^2.1.1 + specifier: 'catalog:' version: 2.1.1 effect: - specifier: ^3.19.15 - version: 3.19.15 + specifier: 'catalog:' + version: 3.19.16 lucide-react: - specifier: ^0.563.0 - version: 0.563.0(react@19.2.3) + specifier: 'catalog:' + version: 0.563.0(react@19.2.4) react: - specifier: ^19.2.0 - version: 19.2.3 + specifier: 'catalog:' + version: 19.2.4 react-dom: - specifier: ^19.2.0 - version: 19.2.3(react@19.2.3) + specifier: 'catalog:' + version: 19.2.4(react@19.2.4) sonner: - specifier: ^2.0.7 - version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 'catalog:' + version: 2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) tailwind-merge: - specifier: ^3.0.2 + specifier: 'catalog:' version: 3.4.0 tailwindcss: - specifier: ^4.0.6 + specifier: 'catalog:' version: 4.1.18 tw-animate-css: - specifier: ^1.3.6 + specifier: 'catalog:' version: 1.4.0 viem: - specifier: ^2.45.0 - version: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + specifier: 'catalog:' + version: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: - specifier: ^3.4.1 - version: 3.4.1(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.3))(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + specifier: 'catalog:' + version: 3.4.2(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.4))(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) devDependencies: - '@biomejs/biome': - specifier: 2.3.12 - version: 2.3.12 - '@effect/language-service': - specifier: ^0.72.0 - version: 0.72.0 '@tanstack/devtools-vite': - specifier: ^0.4.1 - version: 0.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + specifier: 'catalog:' + version: 0.5.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@tanstack/router-cli': - specifier: ^1.157.15 - version: 1.157.15 + specifier: 'catalog:' + version: 1.158.0 + '@tanstack/router-plugin': + specifier: 'catalog:' + version: 1.158.0(@tanstack/react-router@1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@testing-library/dom': - specifier: ^10.4.0 + specifier: 'catalog:' version: 10.4.1 '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 'catalog:' + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tim-smart/openapi-gen': - specifier: ^0.4.13 + specifier: 'catalog:' version: 0.4.13(patch_hash=36720013d0f70c201ce8f233e38a7b93fc9fce74a17f9bc4293c3cf891b4fdc6) '@types/node': - specifier: ^25.0.10 - version: 25.0.10 + specifier: 'catalog:' + version: 25.2.0 '@types/react': - specifier: ^19.2.9 - version: 19.2.9 + specifier: 'catalog:' + version: 19.2.11 '@types/react-dom': - specifier: ^19.2.0 - version: 19.2.3(@types/react@19.2.9) + specifier: 'catalog:' + version: 19.2.3(@types/react@19.2.11) '@vite-pwa/assets-generator': - specifier: ^1.0.2 + specifier: 'catalog:' version: 1.0.2 '@vitejs/plugin-react': - specifier: ^5.0.4 - version: 5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + specifier: 'catalog:' + version: 5.1.3(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/browser-playwright': - specifier: ^4.0.18 - version: 4.0.18(bufferutil@4.1.0)(playwright@1.58.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) + specifier: 'catalog:' + version: 4.0.18(bufferutil@4.1.0)(playwright@1.58.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) babel-plugin-react-compiler: - specifier: ^1.0.0 + specifier: 'catalog:' version: 1.0.0 jsdom: - specifier: ^27.0.0 - version: 27.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 'catalog:' + version: 28.0.0(@noble/hashes@1.8.0) openapi-filter: - specifier: ^3.2.3 + specifier: 'catalog:' version: 3.2.3 tsx: - specifier: ^4.21.0 + specifier: 'catalog:' version: 4.21.0 typescript: - specifier: ^5.7.2 + specifier: 'catalog:' version: 5.9.3 vite: - specifier: ^7.3.1 - version: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + specifier: 'catalog:' + version: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) vite-plugin-node-polyfills: - specifier: ^0.25.0 - version: 0.25.0(rollup@4.56.0)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + specifier: 'catalog:' + version: 0.25.0(rollup@4.57.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + specifier: 'catalog:' + version: 4.0.18(@types/node@25.2.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) vitest-browser-react: - specifier: ^2.0.4 - version: 2.0.4(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vitest@4.0.18) + specifier: 'catalog:' + version: 2.0.5(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18) packages: - '@acemir/cssom@0.9.30': - resolution: {integrity: sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg==} + '@acemir/cssom@0.9.31': + resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} '@adraffy/ens-normalize@1.11.1': resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} @@ -179,46 +664,38 @@ packages: '@asamuzakjp/css-color@4.1.1': resolution: {integrity: sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==} - '@asamuzakjp/dom-selector@6.7.6': - resolution: {integrity: sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==} + '@asamuzakjp/dom-selector@6.7.7': + resolution: {integrity: sha512-8CO/UQ4tzDd7ula+/CVimJIVWez99UJlbMyIgk8xOnhAVPKLnBZmUFYVgugS441v2ZqUq5EnSh6B0Ua0liSFAA==} '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - '@babel/code-frame@7.28.6': resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.5': - resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} '@babel/compat-data@7.28.6': resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.5': - resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} - engines: {node: '>=6.9.0'} - '@babel/core@7.28.6': resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.5': - resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} '@babel/generator@7.28.6': resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.28.6': @@ -229,30 +706,16 @@ packages: resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.3': - resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-module-transforms@7.28.6': resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.27.1': - resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} - engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.28.6': resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} engines: {node: '>=6.9.0'} @@ -269,21 +732,17 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.4': - resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} - engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.6': resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.5': - resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + '@babel/parser@7.28.6': + resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@7.28.6': - resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} hasBin: true @@ -311,38 +770,30 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.28.6': resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} - engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.6': resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.5': - resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} engines: {node: '>=6.9.0'} '@babel/types@7.28.6': resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@base-org/account@2.4.0': resolution: {integrity: sha512-A4Umpi8B9/pqR78D1Yoze4xHyQaujioVRqqO3d6xuDFw9VRtjg6tK3bPlwE0aW+nVH/ntllCpPa2PbI8Rnjcug==} @@ -367,55 +818,59 @@ packages: '@types/react': optional: true - '@biomejs/biome@2.3.12': - resolution: {integrity: sha512-AR7h4aSlAvXj7TAajW/V12BOw2EiS0AqZWV5dGozf4nlLoUF/ifvD0+YgKSskT0ylA6dY1A8AwgP8kZ6yaCQnA==} + '@biomejs/biome@2.3.14': + resolution: {integrity: sha512-QMT6QviX0WqXJCaiqVMiBUCr5WRQ1iFSjvOLoTk6auKukJMvnMzWucXpwZB0e8F00/1/BsS9DzcKgWH+CLqVuA==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.3.12': - resolution: {integrity: sha512-cO6fn+KiMBemva6EARDLQBxeyvLzgidaFRJi8G7OeRqz54kWK0E+uSjgFaiHlc3DZYoa0+1UFE8mDxozpc9ieg==} + '@biomejs/cli-darwin-arm64@2.3.14': + resolution: {integrity: sha512-UJGPpvWJMkLxSRtpCAKfKh41Q4JJXisvxZL8ChN1eNW3m/WlPFJ6EFDCE7YfUb4XS8ZFi3C1dFpxUJ0Ety5n+A==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.3.12': - resolution: {integrity: sha512-/fiF/qmudKwSdvmSrSe/gOTkW77mHHkH8Iy7YC2rmpLuk27kbaUOPa7kPiH5l+3lJzTUfU/t6x1OuIq/7SGtxg==} + '@biomejs/cli-darwin-x64@2.3.14': + resolution: {integrity: sha512-PNkLNQG6RLo8lG7QoWe/hhnMxJIt1tEimoXpGQjwS/dkdNiKBLPv4RpeQl8o3s1OKI3ZOR5XPiYtmbGGHAOnLA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.3.12': - resolution: {integrity: sha512-aqkeSf7IH+wkzFpKeDVPSXy9uDjxtLpYA6yzkYsY+tVjwFFirSuajHDI3ul8en90XNs1NA0n8kgBrjwRi5JeyA==} + '@biomejs/cli-linux-arm64-musl@2.3.14': + resolution: {integrity: sha512-LInRbXhYujtL3sH2TMCH/UBwJZsoGwfQjBrMfl84CD4hL/41C/EU5mldqf1yoFpsI0iPWuU83U+nB2TUUypWeg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [musl] - '@biomejs/cli-linux-arm64@2.3.12': - resolution: {integrity: sha512-nbOsuQROa3DLla5vvsTZg+T5WVPGi9/vYxETm9BOuLHBJN3oWQIg3MIkE2OfL18df1ZtNkqXkH6Yg9mdTPem7A==} + '@biomejs/cli-linux-arm64@2.3.14': + resolution: {integrity: sha512-KT67FKfzIw6DNnUNdYlBg+eU24Go3n75GWK6NwU4+yJmDYFe9i/MjiI+U/iEzKvo0g7G7MZqoyrhIYuND2w8QQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [glibc] - '@biomejs/cli-linux-x64-musl@2.3.12': - resolution: {integrity: sha512-kVGWtupRRsOjvw47YFkk5mLiAdpCPMWBo1jOwAzh+juDpUb2sWarIp+iq+CPL1Wt0LLZnYtP7hH5kD6fskcxmg==} + '@biomejs/cli-linux-x64-musl@2.3.14': + resolution: {integrity: sha512-KQU7EkbBBuHPW3/rAcoiVmhlPtDSGOGRPv9js7qJVpYTzjQmVR+C9Rfcz+ti8YCH+zT1J52tuBybtP4IodjxZQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [musl] - '@biomejs/cli-linux-x64@2.3.12': - resolution: {integrity: sha512-CQtqrJ+qEEI8tgRSTjjzk6wJAwfH3wQlkIGsM5dlecfRZaoT+XCms/mf7G4kWNexrke6mnkRzNy6w8ebV177ow==} + '@biomejs/cli-linux-x64@2.3.14': + resolution: {integrity: sha512-ZsZzQsl9U+wxFrGGS4f6UxREUlgHwmEfu1IrXlgNFrNnd5Th6lIJr8KmSzu/+meSa9f4rzFrbEW9LBBA6ScoMA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [glibc] - '@biomejs/cli-win32-arm64@2.3.12': - resolution: {integrity: sha512-Re4I7UnOoyE4kHMqpgtG6UvSBGBbbtvsOvBROgCCoH7EgANN6plSQhvo2W7OCITvTp7gD6oZOyZy72lUdXjqZg==} + '@biomejs/cli-win32-arm64@2.3.14': + resolution: {integrity: sha512-+IKYkj/pUBbnRf1G1+RlyA3LWiDgra1xpS7H2g4BuOzzRbRB+hmlw0yFsLprHhbbt7jUzbzAbAjK/Pn0FDnh1A==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.3.12': - resolution: {integrity: sha512-qqGVWqNNek0KikwPZlOIoxtXgsNGsX+rgdEzgw82Re8nF02W+E2WokaQhpF5TdBh/D/RQ3TLppH+otp6ztN0lw==} + '@biomejs/cli-win32-x64@2.3.14': + resolution: {integrity: sha512-oizCjdyQ3WJEswpb3Chdngeat56rIdSYK12JI3iI11Mt5T5EXcZ7WLuowzEaFPNJ3zmOQFliMN8QY1Pi+qsfdQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -423,8 +878,77 @@ packages: '@canvas/image-data@1.1.0': resolution: {integrity: sha512-QdObRRjRbcXGmM1tmJ+MrHcaz1MftF2+W7YI+MsphnsCrmtyfS0d5qJbk0MeSbUeyM/jCb0hmnkXPsy026L7dA==} - '@coinbase/cdp-sdk@1.43.1': - resolution: {integrity: sha512-3eXP24p5q68agRgu8grGlF+ASidf3xYSDQtdRuDOFCMZafbqANsjl/JxLwYDmUenRodhxBBJgYJ65nOALP58tA==} + '@coinbase/cdp-sdk@1.44.0': + resolution: {integrity: sha512-0I5O1DzbchR91GAYQAU8lxx6q9DBvN0no9IBwrTKLHW8t5bABMg8dzQ/jrGRd6lr/QFJJW4L0ZSLGae5jsxGWw==} + + '@commitlint/cli@20.4.1': + resolution: {integrity: sha512-uuFKKpc7OtQM+6SRqT+a4kV818o1pS+uvv/gsRhyX7g4x495jg+Q7P0+O9VNGyLXBYP0syksS7gMRDJKcekr6A==} + engines: {node: '>=v18'} + hasBin: true + + '@commitlint/config-conventional@20.4.1': + resolution: {integrity: sha512-0YUvIeBtpi86XriqrR+TCULVFiyYTIOEPjK7tTRMxjcBm1qlzb+kz7IF2WxL6Fq5DaundG8VO37BNgMkMTBwqA==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@20.4.0': + resolution: {integrity: sha512-zShmKTF+sqyNOfAE0vKcqnpvVpG0YX8F9G/ZIQHI2CoKyK+PSdladXMSns400aZ5/QZs+0fN75B//3Q5CHw++w==} + engines: {node: '>=v18'} + + '@commitlint/ensure@20.4.1': + resolution: {integrity: sha512-WLQqaFx1pBooiVvBrA1YfJNFqZF8wS/YGOtr5RzApDbV9tQ52qT5VkTsY65hFTnXhW8PcDfZLaknfJTmPejmlw==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@20.0.0': + resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==} + engines: {node: '>=v18'} + + '@commitlint/format@20.4.0': + resolution: {integrity: sha512-i3ki3WR0rgolFVX6r64poBHXM1t8qlFel1G1eCBvVgntE3fCJitmzSvH5JD/KVJN/snz6TfaX2CLdON7+s4WVQ==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@20.4.1': + resolution: {integrity: sha512-In5EO4JR1lNsAv1oOBBO24V9ND1IqdAJDKZiEpdfjDl2HMasAcT7oA+5BKONv1pRoLG380DGPE2W2RIcUwdgLA==} + engines: {node: '>=v18'} + + '@commitlint/lint@20.4.1': + resolution: {integrity: sha512-g94LrGl/c6UhuhDQqNqU232aslLEN2vzc7MPfQTHzwzM4GHNnEAwVWWnh0zX8S5YXecuLXDwbCsoGwmpAgPWKA==} + engines: {node: '>=v18'} + + '@commitlint/load@20.4.0': + resolution: {integrity: sha512-Dauup/GfjwffBXRJUdlX/YRKfSVXsXZLnINXKz0VZkXdKDcaEILAi9oflHGbfydonJnJAbXEbF3nXPm9rm3G6A==} + engines: {node: '>=v18'} + + '@commitlint/message@20.4.0': + resolution: {integrity: sha512-B5lGtvHgiLAIsK5nLINzVW0bN5hXv+EW35sKhYHE8F7V9Uz1fR4tx3wt7mobA5UNhZKUNgB/+ldVMQE6IHZRyA==} + engines: {node: '>=v18'} + + '@commitlint/parse@20.4.1': + resolution: {integrity: sha512-XNtZjeRcFuAfUnhYrCY02+mpxwY4OmnvD3ETbVPs25xJFFz1nRo/25nHj+5eM+zTeRFvWFwD4GXWU2JEtoK1/w==} + engines: {node: '>=v18'} + + '@commitlint/read@20.4.0': + resolution: {integrity: sha512-QfpFn6/I240ySEGv7YWqho4vxqtPpx40FS7kZZDjUJ+eHxu3azfhy7fFb5XzfTqVNp1hNoI3tEmiEPbDB44+cg==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@20.4.0': + resolution: {integrity: sha512-ay1KM8q0t+/OnlpqXJ+7gEFQNlUtSU5Gxr8GEwnVf2TPN3+ywc5DzL3JCxmpucqxfHBTFwfRMXxPRRnR5Ki20g==} + engines: {node: '>=v18'} + + '@commitlint/rules@20.4.1': + resolution: {integrity: sha512-WtqypKEPbQEuJwJS4aKs0OoJRBKz1HXPBC9wRtzVNH68FLhPWzxXlF09hpUXM9zdYTpm4vAdoTGkWiBgQ/vL0g==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@20.0.0': + resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==} + engines: {node: '>=v18'} + + '@commitlint/top-level@20.4.0': + resolution: {integrity: sha512-NDzq8Q6jmFaIIBC/GG6n1OQEaHdmaAAYdrZRlMgW6glYWGZ+IeuXmiymDvQNXPc82mVxq2KiE3RVpcs+1OeDeA==} + engines: {node: '>=v18'} + + '@commitlint/types@20.4.0': + resolution: {integrity: sha512-aO5l99BQJ0X34ft8b0h7QFkQlqxC6e7ZPVmBKz13xM9O8obDaM1Cld4sQlJDXXU/VFuUzQ30mVtHjVz74TuStw==} + engines: {node: '>=v18'} '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} @@ -450,28 +974,27 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.22': - resolution: {integrity: sha512-qBcx6zYlhleiFfdtzkRgwNC7VVoAwfK76Vmsw5t+PbvtdknO9StgRk7ROvq9so1iqbdW4uLIDAsXRsTfUrIoOw==} - engines: {node: '>=18'} + '@csstools/css-syntax-patches-for-csstree@1.0.26': + resolution: {integrity: sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==} '@csstools/css-tokenizer@3.0.4': resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} - '@effect-atom/atom-react@0.4.6': - resolution: {integrity: sha512-ZvncuSQsCcUrsddD00JcKDMraPge0AiDMGiJqKc7ProvagCVrxB+Emy5uHM0l0SLGUx4X56bFE0d1tp9Z/Bf+g==} + '@effect-atom/atom-react@0.5.0': + resolution: {integrity: sha512-aFfjWi4rEJCqfM12Oi36/EKaDm/W6n4/N6yM5vL0t/QozKhJhK05rQL/GY4XMxlH2eqkQ4ih8jBQa3Yyp0Fiqw==} peerDependencies: effect: ^3.19 react: '>=18 <20' scheduler: '*' - '@effect-atom/atom@0.4.13': - resolution: {integrity: sha512-wYCPDg/vN2IWTqxho7kTTSivh//V2TSAcdkQvLXMJWww8YzhcCGmCiDpT6cefyNXvHj+qZrqYB54mrUqG6EYCA==} + '@effect-atom/atom@0.5.0': + resolution: {integrity: sha512-qg6Qpf+ESi63taFJrufPtYDHghRLXxAte/xgpKqN+m7T6I3Lw1jgiNxR4AJeNG7f2UOkpmPhA8AYdWMgaUU61w==} peerDependencies: - '@effect/experimental': ^0.57.0 - '@effect/platform': ^0.93.0 - '@effect/rpc': ^0.72.1 - effect: ^3.19.0 + '@effect/experimental': ^0.58.0 + '@effect/platform': ^0.94.2 + '@effect/rpc': ^0.73.0 + effect: ^3.19.15 '@effect/cluster@0.56.1': resolution: {integrity: sha512-gnrsH6kfrUjn+82j/bw1IR4yFqJqV8tc7xZvrbJPRgzANycc6K1hu3LMg548uYbUkTzD8YYyqrSatMO1mkQpzw==} @@ -495,8 +1018,8 @@ packages: lmdb: optional: true - '@effect/language-service@0.72.0': - resolution: {integrity: sha512-MWkyTPCXSs5Q3OIBWR3q24SA+ipkdWW7EBJBt6EPUzlzZxjJLXtLBhXpMoCFheSEM0FTWOHT4BRLh5lufsmjVw==} + '@effect/language-service@0.73.0': + resolution: {integrity: sha512-/SGoq50VDm/XwQI6d0cReYcLjwfdqwZv7uEJp92+ssx5vLCsm+QvHf4Ul6l6PYVzorsgAp/b6fhAwI2VSkqcJQ==} hasBin: true '@effect/platform-node-shared@0.57.1': @@ -517,10 +1040,10 @@ packages: '@effect/sql': ^0.49.0 effect: ^3.19.15 - '@effect/platform@0.94.2': - resolution: {integrity: sha512-85vdwpnK4oH/rJ3EuX/Gi2Hkt+K4HvXWr9bxCuqvty9hxyEcRxkJcqTesYrcVoQB6aULb1Za2B0MKoTbvffB3Q==} + '@effect/platform@0.94.3': + resolution: {integrity: sha512-bvTR8xLQoRpKgHuprZDOeQdPkhyVw+WT05iI9jl2s8Qiblyk5Dz2JLwJU+EFeksIBaPYz49xa635Om91T1CefQ==} peerDependencies: - effect: ^3.19.15 + effect: ^3.19.16 '@effect/rpc@0.73.0': resolution: {integrity: sha512-iMPf6tTriz8sK0l5x4koFId8Hz5nFptHYg8WqyjHGIIVLTpZxuiSqhmXZG7FnAs5N2n6uCEws4wWGcIgXNUrFg==} @@ -702,23 +1225,23 @@ packages: cpu: [x64] os: [win32] - '@exodus/bytes@1.8.0': - resolution: {integrity: sha512-8JPn18Bcp8Uo1T82gR8lh2guEOa5KKU/IEKvvdp0sgmi7coPBWf1Doi1EXsGZb2ehc8ym/StJCjffYV+ne7sXQ==} + '@exodus/bytes@1.11.0': + resolution: {integrity: sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@exodus/crypto': ^1.0.0-rc.4 + '@noble/hashes': ^1.8.0 || ^2.0.0 peerDependenciesMeta: - '@exodus/crypto': + '@noble/hashes': optional: true - '@floating-ui/core@1.7.3': - resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + '@floating-ui/core@1.7.4': + resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==} - '@floating-ui/dom@1.7.4': - resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + '@floating-ui/dom@1.7.5': + resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==} - '@floating-ui/react-dom@2.1.6': - resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + '@floating-ui/react-dom@2.1.7': + resolution: {integrity: sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' @@ -752,67 +1275,79 @@ packages: resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.0.4': resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.0.4': resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.0.4': resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.33.5': resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.33.5': resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.33.5': resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.33.5': resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} @@ -876,19 +1411,19 @@ packages: '@lit/reactive-element@2.1.2': resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} - '@lucas-barake/effect-form-react@0.14.0': - resolution: {integrity: sha512-Yxiq/zfaF9ACJEfiKq/QvoRuWdrtRz+6ENZOoVe8tbSok016hWfV4fG373sGXyhr8tyZDMkqoiwwaIx60oCONw==} + '@lucas-barake/effect-form-react@0.18.0': + resolution: {integrity: sha512-XW2jUKzxfR+tSq+RV2zAdV3relOUr32h5qpxhaGsChYXu0sKV08+01rvgH2dh1JQM+NXYVvmWvM9Dlc0FxY/iQ==} peerDependencies: - '@effect-atom/atom': ^0.4.8 - '@effect-atom/atom-react': ^0.4.3 - effect: ^3.19.0 + '@effect-atom/atom': ^0.5.0 + '@effect-atom/atom-react': ^0.5.0 + effect: ^3.19.15 react: ^18.0.0 || ^19.0.0 - '@lucas-barake/effect-form@0.14.0': - resolution: {integrity: sha512-mXf7wBHAydUeOFUj8VDWG9eIQVxWLtbv3p2KidrzQlxGCx9mZpYIpRbrDmfsBAGOYC5SVSTIQd8KUvxdXMNaoQ==} + '@lucas-barake/effect-form@0.18.0': + resolution: {integrity: sha512-ecJM7fCzmYKmxUMvO/JT74CXG1ZokxeNoQLQcNF2VnouVKfT+4Bc+fBNc1ANDJKjcj2IjJWMjnnyQ2w1dupsUA==} peerDependencies: - '@effect-atom/atom': ^0.4.8 - effect: ^3.19.0 + '@effect-atom/atom': ^0.5.0 + effect: ^3.19.15 '@msgpack/msgpack@3.1.2': resolution: {integrity: sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==} @@ -987,36 +1522,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.6': resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.6': resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.6': resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.6': resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.6': resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.5.6': resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} @@ -1085,8 +1626,8 @@ packages: '@reown/appkit@1.8.17': resolution: {integrity: sha512-svov4ShvEi4YboVe+kXT8xGQvDrYsgQBrBmccOel9nT7/lOEDUimFu5Irna8g/8Zji9/XbRrYi49cLPJrzd45Q==} - '@rolldown/pluginutils@1.0.0-beta.53': - resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} + '@rolldown/pluginutils@1.0.0-rc.2': + resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==} '@rollup/plugin-inject@5.0.5': resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} @@ -1106,128 +1647,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.56.0': - resolution: {integrity: sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==} + '@rollup/rollup-android-arm-eabi@4.57.1': + resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.56.0': - resolution: {integrity: sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q==} + '@rollup/rollup-android-arm64@4.57.1': + resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.56.0': - resolution: {integrity: sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w==} + '@rollup/rollup-darwin-arm64@4.57.1': + resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.56.0': - resolution: {integrity: sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g==} + '@rollup/rollup-darwin-x64@4.57.1': + resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.56.0': - resolution: {integrity: sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ==} + '@rollup/rollup-freebsd-arm64@4.57.1': + resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.56.0': - resolution: {integrity: sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg==} + '@rollup/rollup-freebsd-x64@4.57.1': + resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.56.0': - resolution: {integrity: sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==} + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': + resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} cpu: [arm] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.56.0': - resolution: {integrity: sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==} + '@rollup/rollup-linux-arm-musleabihf@4.57.1': + resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} cpu: [arm] os: [linux] + libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.56.0': - resolution: {integrity: sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==} + '@rollup/rollup-linux-arm64-gnu@4.57.1': + resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} cpu: [arm64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.56.0': - resolution: {integrity: sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==} + '@rollup/rollup-linux-arm64-musl@4.57.1': + resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} cpu: [arm64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.56.0': - resolution: {integrity: sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==} + '@rollup/rollup-linux-loong64-gnu@4.57.1': + resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} cpu: [loong64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.56.0': - resolution: {integrity: sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==} + '@rollup/rollup-linux-loong64-musl@4.57.1': + resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} cpu: [loong64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.56.0': - resolution: {integrity: sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==} + '@rollup/rollup-linux-ppc64-gnu@4.57.1': + resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} cpu: [ppc64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.56.0': - resolution: {integrity: sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==} + '@rollup/rollup-linux-ppc64-musl@4.57.1': + resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} cpu: [ppc64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.56.0': - resolution: {integrity: sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==} + '@rollup/rollup-linux-riscv64-gnu@4.57.1': + resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} cpu: [riscv64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.56.0': - resolution: {integrity: sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==} + '@rollup/rollup-linux-riscv64-musl@4.57.1': + resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} cpu: [riscv64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.56.0': - resolution: {integrity: sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==} + '@rollup/rollup-linux-s390x-gnu@4.57.1': + resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} cpu: [s390x] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.56.0': - resolution: {integrity: sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==} + '@rollup/rollup-linux-x64-gnu@4.57.1': + resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} cpu: [x64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.56.0': - resolution: {integrity: sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==} + '@rollup/rollup-linux-x64-musl@4.57.1': + resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} cpu: [x64] os: [linux] + libc: [musl] - '@rollup/rollup-openbsd-x64@4.56.0': - resolution: {integrity: sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==} + '@rollup/rollup-openbsd-x64@4.57.1': + resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.56.0': - resolution: {integrity: sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ==} + '@rollup/rollup-openharmony-arm64@4.57.1': + resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.56.0': - resolution: {integrity: sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing==} + '@rollup/rollup-win32-arm64-msvc@4.57.1': + resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.56.0': - resolution: {integrity: sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg==} + '@rollup/rollup-win32-ia32-msvc@4.57.1': + resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.56.0': - resolution: {integrity: sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ==} + '@rollup/rollup-win32-x64-gnu@4.57.1': + resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.56.0': - resolution: {integrity: sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g==} + '@rollup/rollup-win32-x64-msvc@4.57.1': + resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} cpu: [x64] os: [win32] @@ -1260,8 +1814,8 @@ packages: peerDependencies: '@solana/kit': ^5.0 - '@solana/accounts@5.4.0': - resolution: {integrity: sha512-qHtAtwCcCFTXcya6JOOG1nzYicivivN/JkcYNHr10qOp9b4MVRkfW1ZAAG1CNzjMe5+mwtEl60RwdsY9jXNb+Q==} + '@solana/accounts@5.5.1': + resolution: {integrity: sha512-TfOY9xixg5rizABuLVuZ9XI2x2tmWUC/OoN556xwfDlhBHBjKfszicYYOyD6nbFmwTGYarCmyGIdteXxTXIdhQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1269,8 +1823,8 @@ packages: typescript: optional: true - '@solana/addresses@5.4.0': - resolution: {integrity: sha512-YRHiH30S8qDV4bZ+mtEk589PGfBuXHzD/fK2Z+YI5f/+s+yi/5le/fVw7PN6LxnnmVQKiRCDUiNF+WmFFKi6QQ==} + '@solana/addresses@5.5.1': + resolution: {integrity: sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1278,8 +1832,8 @@ packages: typescript: optional: true - '@solana/assertions@5.4.0': - resolution: {integrity: sha512-8EP7mkdnrPc9y67FqWeAPzdWq2qAOkxsuo+ZBIXNWtIixDtXIdHrgjZ/wqbWxLgSTtXEfBCjpZU55Xw2Qfbwyg==} + '@solana/assertions@5.5.1': + resolution: {integrity: sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1297,8 +1851,8 @@ packages: peerDependencies: typescript: '>=5.3.3' - '@solana/codecs-core@5.4.0': - resolution: {integrity: sha512-rQ5jXgiDe2vIU+mYCHDjgwMd9WdzZfh4sc5H6JgYleAUjeTUX6mx8hTV2+pcXvvn27LPrgrt9jfxswbDb8O8ww==} + '@solana/codecs-core@5.5.1': + resolution: {integrity: sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1306,8 +1860,8 @@ packages: typescript: optional: true - '@solana/codecs-data-structures@5.4.0': - resolution: {integrity: sha512-LVssbdQ1GfY6upnxW3mufYsNfvTWKnHNk5Hx2gHuOYJhm3HZlp+Y8zvuoY65G1d1xAXkPz5YVGxaSeVIRWLGWg==} + '@solana/codecs-data-structures@5.5.1': + resolution: {integrity: sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1321,8 +1875,8 @@ packages: peerDependencies: typescript: '>=5.3.3' - '@solana/codecs-numbers@5.4.0': - resolution: {integrity: sha512-z6LMkY+kXWx1alrvIDSAxexY5QLhsso638CjM7XI1u6dB7drTLWKhifyjnm1vOQc1VPVFmbYxTgKKpds8TY8tg==} + '@solana/codecs-numbers@5.5.1': + resolution: {integrity: sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1330,8 +1884,8 @@ packages: typescript: optional: true - '@solana/codecs-strings@5.4.0': - resolution: {integrity: sha512-w0trrjfQDhkCVz7O1GTmHBk9m+MkljKx2uNBbQAD3/yW2Qn9dYiTrZ1/jDVq0/+lPPAUkbT3s3Yo7HUZ2QFmHw==} + '@solana/codecs-strings@5.5.1': + resolution: {integrity: sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A==} engines: {node: '>=20.18.0'} peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 @@ -1342,8 +1896,8 @@ packages: typescript: optional: true - '@solana/codecs@5.4.0': - resolution: {integrity: sha512-IbDCUvNX0MrkQahxiXj9rHzkd/fYfp1F2nTJkHGH8v+vPfD+YPjl007ZBM38EnCeXj/Xn+hxqBBivPvIHP29dA==} + '@solana/codecs@5.5.1': + resolution: {integrity: sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1358,8 +1912,8 @@ packages: peerDependencies: typescript: '>=5.3.3' - '@solana/errors@5.4.0': - resolution: {integrity: sha512-hNoAOmlZAszaVBrAy1Jf7amHJ8wnUnTU0BqhNQXknbSvirvsYr81yEud2iq18YiCqhyJ9SuQ5kWrSAT0x7S0oA==} + '@solana/errors@5.5.1': + resolution: {integrity: sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg==} engines: {node: '>=20.18.0'} hasBin: true peerDependencies: @@ -1368,8 +1922,8 @@ packages: typescript: optional: true - '@solana/fast-stable-stringify@5.4.0': - resolution: {integrity: sha512-KB7PUL7yalPvbWCezzyUDVRDp39eHLPH7OJ6S8VFT8YNIFUANwwj5ctui50Fim76kvSYDdYJOclXV45O2gfQ8Q==} + '@solana/fast-stable-stringify@5.5.1': + resolution: {integrity: sha512-Ni7s2FN33zTzhTFgRjEbOVFO+UAmK8qi3Iu0/GRFYK4jN696OjKHnboSQH/EacQ+yGqS54bfxf409wU5dsLLCw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1377,8 +1931,8 @@ packages: typescript: optional: true - '@solana/functional@5.4.0': - resolution: {integrity: sha512-32ghHO0bg6GgX/7++0/7Lps6RgeXD2gKF1okiuyEGuVfKENIapgaQdcGhUwb3q6D6fv6MRAVn/Yve4jopGVNMQ==} + '@solana/functional@5.5.1': + resolution: {integrity: sha512-tTHoJcEQq3gQx5qsdsDJ0LEJeFzwNpXD80xApW9o/PPoCNimI3SALkZl+zNW8VnxRrV3l3yYvfHWBKe/X3WG3w==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1386,8 +1940,8 @@ packages: typescript: optional: true - '@solana/instruction-plans@5.4.0': - resolution: {integrity: sha512-5xbJ+I/pP2aWECmK75bEM1zCnIITlohAK83dVN+t5X2vBFrr6M9gifo8r4Opdnibsgo6QVVkKPxRo5zow5j0ig==} + '@solana/instruction-plans@5.5.1': + resolution: {integrity: sha512-7z3CB7YMcFKuVvgcnNY8bY6IsZ8LG61Iytbz7HpNVGX2u1RthOs1tRW8luTzSG1MPL0Ox7afyAVMYeFqSPHnaQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1395,8 +1949,8 @@ packages: typescript: optional: true - '@solana/instructions@5.4.0': - resolution: {integrity: sha512-//a7jpHbNoAgTqy3YyqG1X6QhItJLKzJa6zuYJGCwaAAJye7BxS9pxJBgb2mUt7CGidhUksf+U8pmLlxCNWYyg==} + '@solana/instructions@5.5.1': + resolution: {integrity: sha512-h0G1CG6S+gUUSt0eo6rOtsaXRBwCq1+Js2a+Ps9Bzk9q7YHNFA75/X0NWugWLgC92waRp66hrjMTiYYnLBoWOQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1404,8 +1958,8 @@ packages: typescript: optional: true - '@solana/keys@5.4.0': - resolution: {integrity: sha512-zQVbAwdoXorgXjlhlVTZaymFG6N8n1zn2NT+xI6S8HtbrKIB/42xPdXFh+zIihGzRw+9k8jzU7Axki/IPm6qWQ==} + '@solana/keys@5.5.1': + resolution: {integrity: sha512-KRD61cL7CRL+b4r/eB9dEoVxIf/2EJ1Pm1DmRYhtSUAJD2dJ5Xw8QFuehobOGm9URqQ7gaQl+Fkc1qvDlsWqKg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1413,8 +1967,8 @@ packages: typescript: optional: true - '@solana/kit@5.4.0': - resolution: {integrity: sha512-aVjN26jOEzJA6UBYxSTQciZPXgTxWnO/WysHrw+yeBL/5AaTZnXEgb4j5xV6cUFzOlVxhJBrx51xtoxSqJ0u3g==} + '@solana/kit@5.5.1': + resolution: {integrity: sha512-irKUGiV2yRoyf+4eGQ/ZeCRxa43yjFEL1DUI5B0DkcfZw3cr0VJtVJnrG8OtVF01vT0OUfYOcUn6zJW5TROHvQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1422,8 +1976,8 @@ packages: typescript: optional: true - '@solana/nominal-types@5.4.0': - resolution: {integrity: sha512-h4dTRQwTerzksE5B1WmObN6TvLo8dYUd7kpUUynGd8WJjK0zz3zkDhq0MkA3aF6A1C2C82BSGqSsN9EN0E6Exg==} + '@solana/nominal-types@5.5.1': + resolution: {integrity: sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1431,8 +1985,8 @@ packages: typescript: optional: true - '@solana/offchain-messages@5.4.0': - resolution: {integrity: sha512-DjdlYJCcKfgh4dkdk+owH1bP+Q4BRqCs55mgWWp9PTwm/HHy/a5vcMtCi1GyIQXfhtNNvKBLbXrUE0Fxej8qlg==} + '@solana/offchain-messages@5.5.1': + resolution: {integrity: sha512-g+xHH95prTU+KujtbOzj8wn+C7ZNoiLhf3hj6nYq3MTyxOXtBEysguc97jJveUZG0K97aIKG6xVUlMutg5yxhw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1440,8 +1994,8 @@ packages: typescript: optional: true - '@solana/options@5.4.0': - resolution: {integrity: sha512-h4vTWRChEXPhaHo9i1pCyQBWWs+NqYPQRXSAApqpUYvHb9Kct/C6KbHjfyaRMyqNQnDHLcJCX7oW9tk0iRDzIg==} + '@solana/options@5.5.1': + resolution: {integrity: sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1449,8 +2003,8 @@ packages: typescript: optional: true - '@solana/plugin-core@5.4.0': - resolution: {integrity: sha512-e1aLGLldW7C5113qTOjFYSGq95a4QC9TWb77iq+8l6h085DcNj+195r4E2zKaINrevQjQTwvxo00oUyHP7hSJA==} + '@solana/plugin-core@5.5.1': + resolution: {integrity: sha512-VUZl30lDQFJeiSyNfzU1EjYt2QZvoBFKEwjn1lilUJw7KgqD5z7mbV7diJhT+dLFs36i0OsjXvq5kSygn8YJ3A==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1458,8 +2012,8 @@ packages: typescript: optional: true - '@solana/programs@5.4.0': - resolution: {integrity: sha512-Sc90WK9ZZ7MghOflIvkrIm08JwsFC99yqSJy28/K+hDP2tcx+1x+H6OFP9cumW9eUA1+JVRDeKAhA8ak7e/kUA==} + '@solana/programs@5.5.1': + resolution: {integrity: sha512-7U9kn0Jsx1NuBLn5HRTFYh78MV4XN145Yc3WP/q5BlqAVNlMoU9coG5IUTJIG847TUqC1lRto3Dnpwm6T4YRpA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1467,8 +2021,8 @@ packages: typescript: optional: true - '@solana/promises@5.4.0': - resolution: {integrity: sha512-23mfgNBbuP6Q+4vsixGy+GkyZ7wBLrxTBNXqrG/XWrJhjuuSkjEUGaK4Fx5o7LIrBi6KGqPknKxmTlvqnJhy2Q==} + '@solana/promises@5.5.1': + resolution: {integrity: sha512-T9lfuUYkGykJmppEcssNiCf6yiYQxJkhiLPP+pyAc2z84/7r3UVIb2tNJk4A9sucS66pzJnVHZKcZVGUUp6wzA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1476,8 +2030,8 @@ packages: typescript: optional: true - '@solana/rpc-api@5.4.0': - resolution: {integrity: sha512-FJL6KaAsQ4DhfhLKKMcqbTpToNFwHlABCemIpOunE3OSqJFDrmc/NbsEaLIoeHyIg3d1Imo49GIUOn2TEouFUA==} + '@solana/rpc-api@5.5.1': + resolution: {integrity: sha512-XWOQQPhKl06Vj0xi3RYHAc6oEQd8B82okYJ04K7N0Vvy3J4PN2cxeK7klwkjgavdcN9EVkYCChm2ADAtnztKnA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1485,8 +2039,8 @@ packages: typescript: optional: true - '@solana/rpc-parsed-types@5.4.0': - resolution: {integrity: sha512-IRQuSzx+Sj1A3XGiIzguNZlMjMMybXTTjV/RnTwBgnJQPd/H4us4pfPD94r+/yolWDVfGjJRm04hnKVMjJU8Rg==} + '@solana/rpc-parsed-types@5.5.1': + resolution: {integrity: sha512-HEi3G2nZqGEsa3vX6U0FrXLaqnUCg4SKIUrOe8CezD+cSFbRTOn3rCLrUmJrhVyXlHoQVaRO9mmeovk31jWxJg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1494,8 +2048,8 @@ packages: typescript: optional: true - '@solana/rpc-spec-types@5.4.0': - resolution: {integrity: sha512-JU9hC5/iyJx30ym17gpoXDtT9rCbO6hLpB6UDhSFFoNeirxtTVb4OdnKtsjJDfXAiXsynJRsZRwfj3vGxRLgQw==} + '@solana/rpc-spec-types@5.5.1': + resolution: {integrity: sha512-6OFKtRpIEJQs8Jb2C4OO8KyP2h2Hy1MFhatMAoXA+0Ik8S3H+CicIuMZvGZ91mIu/tXicuOOsNNLu3HAkrakrw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1503,8 +2057,8 @@ packages: typescript: optional: true - '@solana/rpc-spec@5.4.0': - resolution: {integrity: sha512-XMhxBb1GuZ3Kaeu5WNHB5KteCQ/aVuMByZmUKPqaanD+gs5MQZr0g62CvN7iwRlFU7GC18Q73ROWR3/JjzbXTA==} + '@solana/rpc-spec@5.5.1': + resolution: {integrity: sha512-m3LX2bChm3E3by4mQrH4YwCAFY57QBzuUSWqlUw7ChuZ+oLLOq7b2czi4i6L4Vna67j3eCmB3e+4tqy1j5wy7Q==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1512,8 +2066,8 @@ packages: typescript: optional: true - '@solana/rpc-subscriptions-api@5.4.0': - resolution: {integrity: sha512-euAFIG6ruEsqK+MsrL1tGSMbbOumm8UAyGzlD/kmXsAqqhcVsSeZdv5+BMIHIBsQ93GHcloA8UYw1BTPhpgl9w==} + '@solana/rpc-subscriptions-api@5.5.1': + resolution: {integrity: sha512-5Oi7k+GdeS8xR2ly1iuSFkAv6CZqwG0Z6b1QZKbEgxadE1XGSDrhM2cn59l+bqCozUWCqh4c/A2znU/qQjROlw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1521,8 +2075,8 @@ packages: typescript: optional: true - '@solana/rpc-subscriptions-channel-websocket@5.4.0': - resolution: {integrity: sha512-kWCmlW65MccxqXwKsIz+LkXUYQizgvBrrgYOkyclJHPa+zx4gqJjam87+wzvO9cfbDZRer3wtJBaRm61gTHNbw==} + '@solana/rpc-subscriptions-channel-websocket@5.5.1': + resolution: {integrity: sha512-7tGfBBrYY8TrngOyxSHoCU5shy86iA9SRMRrPSyBhEaZRAk6dnbdpmUTez7gtdVo0BCvh9nzQtUycKWSS7PnFQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1530,8 +2084,8 @@ packages: typescript: optional: true - '@solana/rpc-subscriptions-spec@5.4.0': - resolution: {integrity: sha512-ELaV9Z39GtKyUO0++he00ymWleb07QXYJhSfA0e1N5Q9hXu/Y366kgXHDcbZ/oUJkT3ylNgTupkrsdtiy8Ryow==} + '@solana/rpc-subscriptions-spec@5.5.1': + resolution: {integrity: sha512-iq+rGq5fMKP3/mKHPNB6MC8IbVW41KGZg83Us/+LE3AWOTWV1WT20KT2iH1F1ik9roi42COv/TpoZZvhKj45XQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1539,8 +2093,8 @@ packages: typescript: optional: true - '@solana/rpc-subscriptions@5.4.0': - resolution: {integrity: sha512-051t1CEjjAzM9ohjj2zb3ED70yeS3ZY8J5wSytL6tthTGImw/JB2a0D9DWMOKriFKt496n95IC+IdpJ35CpBWA==} + '@solana/rpc-subscriptions@5.5.1': + resolution: {integrity: sha512-CTMy5bt/6mDh4tc6vUJms9EcuZj3xvK0/xq8IQ90rhkpYvate91RjBP+egvjgSayUg9yucU9vNuUpEjz4spM7w==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1548,8 +2102,8 @@ packages: typescript: optional: true - '@solana/rpc-transformers@5.4.0': - resolution: {integrity: sha512-dZ8keYloLW+eRAwAPb471uWCFs58yHloLoI+QH0FulYpsSJ7F2BNWYcdnjSS/WiggsNcU6DhpWzYAzlEY66lGQ==} + '@solana/rpc-transformers@5.5.1': + resolution: {integrity: sha512-OsWqLCQdcrRJKvHiMmwFhp9noNZ4FARuMkHT5us3ustDLXaxOjF0gfqZLnMkulSLcKt7TGXqMhBV+HCo7z5M8Q==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1557,8 +2111,8 @@ packages: typescript: optional: true - '@solana/rpc-transport-http@5.4.0': - resolution: {integrity: sha512-vidA+Qtqrnqp3QSVumWHdWJ/986yCr5+qX3fbc9KPm9Ofoto88OMWB/oLJvi2Tfges1UBu/jl+lJdsVckCM1bA==} + '@solana/rpc-transport-http@5.5.1': + resolution: {integrity: sha512-yv8GoVSHqEV0kUJEIhkdOVkR2SvJ6yoWC51cJn2rSV7plr6huLGe0JgujCmB7uZhhaLbcbP3zxXxu9sOjsi7Fg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1566,8 +2120,8 @@ packages: typescript: optional: true - '@solana/rpc-types@5.4.0': - resolution: {integrity: sha512-+C4N4/5AYzBdt3Y2yzkScknScy/jTx6wfvuJIY9XjOXtdDyZ8TmrnMwdPMTZPGLdLuHplJwlwy1acu/4hqmrBQ==} + '@solana/rpc-types@5.5.1': + resolution: {integrity: sha512-bibTFQ7PbHJJjGJPmfYC2I+/5CRFS4O2p9WwbFraX1Keeel+nRrt/NBXIy8veP5AEn2sVJIyJPpWBRpCx1oATA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1575,8 +2129,8 @@ packages: typescript: optional: true - '@solana/rpc@5.4.0': - resolution: {integrity: sha512-S6GRG+usnubDs0JSpgc0ZWEh9IPL5KPWMuBoD8ggGVOIVWntp53FpvhYslNzbxWBXlTvJecr2todBipGVM/AqQ==} + '@solana/rpc@5.5.1': + resolution: {integrity: sha512-ku8zTUMrkCWci66PRIBC+1mXepEnZH/q1f3ck0kJZ95a06bOTl5KU7HeXWtskkyefzARJ5zvCs54AD5nxjQJ+A==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1584,8 +2138,8 @@ packages: typescript: optional: true - '@solana/signers@5.4.0': - resolution: {integrity: sha512-s+fZxpi6UPr6XNk2pH/R84WjNRoSktrgG8AGNfsj/V8MJ++eKX7hhIf4JsHZtnnQXXrHmS3ozB2oHlc8yEJvCQ==} + '@solana/signers@5.5.1': + resolution: {integrity: sha512-FY0IVaBT2kCAze55vEieR6hag4coqcuJ31Aw3hqRH7mv6sV8oqwuJmUrx+uFwOp1gwd5OEAzlv6N4hOOple4sQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1593,8 +2147,8 @@ packages: typescript: optional: true - '@solana/subscribable@5.4.0': - resolution: {integrity: sha512-72LmfNX7UENgA24sn/xjlWpPAOsrxkWb9DQhuPZxly/gq8rl/rvr7Xu9qBkvFF2po9XpdUrKlccqY4awvfpltA==} + '@solana/subscribable@5.5.1': + resolution: {integrity: sha512-9K0PsynFq0CsmK1CDi5Y2vUIJpCqkgSS5yfDN0eKPgHqEptLEaia09Kaxc90cSZDZU5mKY/zv1NBmB6Aro9zQQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1602,8 +2156,8 @@ packages: typescript: optional: true - '@solana/sysvars@5.4.0': - resolution: {integrity: sha512-A5NES7sOlFmpnsiEts5vgyL3NXrt/tGGVSEjlEGvsgwl5EDZNv+xWnNA400uMDqd9O3a5PmH7p/6NsgR+kUzSg==} + '@solana/sysvars@5.5.1': + resolution: {integrity: sha512-k3Quq87Mm+geGUu1GWv6knPk0ALsfY6EKSJGw9xUJDHzY/RkYSBnh0RiOrUhtFm2TDNjOailg8/m0VHmi3reFA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1611,8 +2165,8 @@ packages: typescript: optional: true - '@solana/transaction-confirmation@5.4.0': - resolution: {integrity: sha512-EdSDgxs84/4gkjQw2r7N+Kgus8x9U+NFo0ufVG+48V8Hzy2t0rlBuXgIxwx0zZwUuTIgaKhpIutJgVncwZ5koA==} + '@solana/transaction-confirmation@5.5.1': + resolution: {integrity: sha512-j4mKlYPHEyu+OD7MBt3jRoX4ScFgkhZC6H65on4Fux6LMScgivPJlwnKoZMnsgxFgWds0pl+BYzSiALDsXlYtw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1620,8 +2174,8 @@ packages: typescript: optional: true - '@solana/transaction-messages@5.4.0': - resolution: {integrity: sha512-qd/3kZDaPiHM0amhn3vXnupfcsFTVz6CYuHXvq9HFv/fq32+5Kp1FMLnmHwoSxQxdTMDghPdOhC4vhNhuWmuVQ==} + '@solana/transaction-messages@5.5.1': + resolution: {integrity: sha512-aXyhMCEaAp3M/4fP0akwBBQkFPr4pfwoC5CLDq999r/FUwDax2RE/h4Ic7h2Xk+JdcUwsb+rLq85Y52hq84XvQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1629,8 +2183,8 @@ packages: typescript: optional: true - '@solana/transactions@5.4.0': - resolution: {integrity: sha512-OuY4M4x/xna8KZQIrz8tSrI9EEul9Od97XejqFmGGkEjbRsUOfJW8705TveTW8jU3bd5RGecFYscPgS2F+m7jQ==} + '@solana/transactions@5.5.1': + resolution: {integrity: sha512-8hHtDxtqalZ157pnx6p8k10D7J/KY/biLzfgh9R09VNLLY3Fqi7kJvJCr7M2ik3oRll56pxhraAGCC9yIT6eOA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1727,24 +2281,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.1.18': resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.1.18': resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.1.18': resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.1.18': resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} @@ -1797,14 +2355,14 @@ packages: peerDependencies: solid-js: '>=1.9.7' - '@tanstack/devtools-vite@0.4.1': - resolution: {integrity: sha512-PkMOomcWnl/pUkCqIjqL/csjPHtkMVBirDpJVOZR7XJZDxo5CuD7B+3KsujFCF4Dsn6QYlae97gCZvxi/CB76Q==} + '@tanstack/devtools-vite@0.5.0': + resolution: {integrity: sha512-Ew+ZdTnmTlVjm4q+/XY/dolx/E1BWMYpiRDyU/MXqHf5epri4MLl5C4UZJaO+ZuUCsKPpsW+ufoM99E2Z4rhug==} engines: {node: '>=18'} peerDependencies: vite: ^6.0.0 || ^7.0.0 - '@tanstack/devtools@0.10.3': - resolution: {integrity: sha512-M2HnKtaNf3Z8JDTNDq+X7/1gwOqSwTnCyC0GR+TYiRZM9mkY9GpvTqp6p6bx3DT8onu2URJiVxgHD9WK2e3MNQ==} + '@tanstack/devtools@0.10.5': + resolution: {integrity: sha512-aptV4sMcdEn/zB8zqNqKSKi8pLzfB7BhdP2MuVmyfWgBDYNchqJjhviaxEXW3tJTolbWwc30o+jszwqxOIcIaA==} engines: {node: '>=18'} peerDependencies: solid-js: '>=1.9.7' @@ -1816,8 +2374,8 @@ packages: '@tanstack/query-core@5.90.20': resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} - '@tanstack/react-devtools@0.9.2': - resolution: {integrity: sha512-JNXvBO3jgq16GzTVm7p65n5zHNfMhnqF6Bm7CawjoqZrjEakxbM6Yvy63aKSIpbrdf+Wun2Xn8P0qD+vp56e1g==} + '@tanstack/react-devtools@0.9.4': + resolution: {integrity: sha512-6wQf8gVKDks1VL+LI5SS4XWK8dQLIjcDF3iMZfidyesWJNmodWbWlRkdgCmK5SpDSbcygjbp3p+LG2nE/SZ1bQ==} engines: {node: '>=18'} peerDependencies: '@types/react': '>=16.8' @@ -1830,20 +2388,20 @@ packages: peerDependencies: react: ^18 || ^19 - '@tanstack/react-router-devtools@1.157.8': - resolution: {integrity: sha512-cD7pJ+IpNBWsHv91bLamdUf5/jByy/He7nSveC/LCvc7mK2sssTQeblhg5km3GeNmBqW7kI6Ah/Rh+LmBBZvgA==} + '@tanstack/react-router-devtools@1.158.0': + resolution: {integrity: sha512-uhciBlsPW67xbDCFyc2RQS00OergfNXYxendGUO2HGy4uTr79aQSCb4l6v+sdlPwUTlzwkAtM6e1illIexF15Q==} engines: {node: '>=12'} peerDependencies: - '@tanstack/react-router': ^1.157.8 - '@tanstack/router-core': ^1.157.8 + '@tanstack/react-router': ^1.158.0 + '@tanstack/router-core': ^1.158.0 react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' peerDependenciesMeta: '@tanstack/router-core': optional: true - '@tanstack/react-router@1.157.8': - resolution: {integrity: sha512-kl95/8/wQz6bajOg/+HVbQcIDmv0K+M2EflYmulUThyUSWh2ME4OLKDMpTjPpUMQcwsSV/3/Fp9AiUps3Bh+fw==} + '@tanstack/react-router@1.158.0': + resolution: {integrity: sha512-kvTaO6zjq9WWPyo1wwSZx95AjJ9KOvu23cOMgKeDdDQWKF3Z9q3fwhToKMKJoC11T2Vuivz+o/anrxCcOvdRzw==} engines: {node: '>=12'} peerDependencies: react: '>=18.0.0 || >=19.0.0' @@ -1861,43 +2419,35 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/router-cli@1.157.15': - resolution: {integrity: sha512-a3ni8JUh9svDd4IenJx+9za/Gd1gHzYi4iH36Lf/hgf+zPl6ZBK6cGsveVo2xRVz94iWgpDSWOkgXC0d9fuJ7w==} + '@tanstack/router-cli@1.158.0': + resolution: {integrity: sha512-HydE08MrYY93JQhoB377YOONmtNAhPUw/6pdIUmnRICUx2GV2EK+qXGJ67SDC4YIU/z9X0TDeoZjCO1MDFwiQQ==} engines: {node: '>=12'} hasBin: true - '@tanstack/router-core@1.157.15': - resolution: {integrity: sha512-KaYz6s+wYcg92kRQ7HXlTJLhBaBXOYiiqRBv5tsRbKRIqqhWNyeGz5+NfDwaYFHg5XLSDs3DvN0elMtxcj4dTg==} + '@tanstack/router-core@1.158.0': + resolution: {integrity: sha512-dRMcWY0UB/6OZqSCx/7iUvom0ol18rHSQladygVT8mlth7uxYx3n5BNse8C03efIE8y1Bx+VDOBAKpAZ9BgKog==} engines: {node: '>=12'} - '@tanstack/router-core@1.157.8': - resolution: {integrity: sha512-2fxuhHIZ3yBXN9rxcDOgCsezuwk1VoWvprIDMe+HDi6FBdpWgEREMp0CWEYn33Lxla44iwaTpwvVcFDk8QC/rA==} - engines: {node: '>=12'} - - '@tanstack/router-devtools-core@1.157.8': - resolution: {integrity: sha512-sFVaw7nPQu/U1/y6jEkoMuO9GJ/8OEaDpAnD5zeXHSlZpuhwtwuPnyI02XA75JSrLp4hZUj46BvF5E4uW3ZA+w==} + '@tanstack/router-devtools-core@1.158.0': + resolution: {integrity: sha512-8FUKfjh8Xz9T9O5yYaiVE0Va5aCMncQyVPKb7yy5M/buDnx9Kh0bPjw/eUZJWftOyxW6/WeR605yjOdx/PnqNw==} engines: {node: '>=12'} peerDependencies: - '@tanstack/router-core': ^1.157.8 + '@tanstack/router-core': ^1.158.0 csstype: ^3.0.10 peerDependenciesMeta: csstype: optional: true - '@tanstack/router-generator@1.157.15': - resolution: {integrity: sha512-zGac6tyRFz/X86fk9/CAmS6z8lyZf4p9lhAqLBCKVkFiFPmU4eAJp1ODvs81EtV0uJdRL1/rb+uvgHLGUsmQ0g==} - engines: {node: '>=12'} - - '@tanstack/router-generator@1.157.8': - resolution: {integrity: sha512-P0uvoFkhqrkmn/npgJJ02aGGsw2CMtoEAWQkiJGg00aXyA+026Ny1G/4FPuYsGK11c6yMsOl6FHk/sipNY+2YA==} + '@tanstack/router-generator@1.158.0': + resolution: {integrity: sha512-hVkXQSN/fMD9q3Zn3wNa4PV0Y9VNwQB2bLaecA5i4vc43GX75vmgyiKoMr44BJheUssfVoL/po9a/7sv+N6lKA==} engines: {node: '>=12'} - '@tanstack/router-plugin@1.157.8': - resolution: {integrity: sha512-k3pMbV4DGBAf9rjZL5bdPUAPiFypV4ys6UB8JAK/9EO6zdaF0BksD8KxWgJypuxaaeCMJL/RspOUqCmwbwAXVA==} + '@tanstack/router-plugin@1.158.0': + resolution: {integrity: sha512-FxTOo/icU373jlOu9nlzR0B1vqc47tKZLw3HwOQwCBL4P4EihOBz+L7dcGyKR8bRBL0rCRWvHQTSHNMOr+fGYQ==} engines: {node: '>=12'} peerDependencies: '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.157.8 + '@tanstack/react-router': ^1.158.0 vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' vite-plugin-solid: ^2.11.10 webpack: '>=5.92.0' @@ -1913,8 +2463,8 @@ packages: webpack: optional: true - '@tanstack/router-utils@1.154.7': - resolution: {integrity: sha512-61bGx32tMKuEpVRseu2sh1KQe8CfB7793Mch/kyQt0EP3tD7X0sXmimCl3truRiDGUtI0CaSoQV1NPjAII1RBA==} + '@tanstack/router-utils@1.158.0': + resolution: {integrity: sha512-qZ76eaLKU6Ae9iI/mc5zizBX149DXXZkBVVO3/QRIll79uKLJZHQlMKR++2ba7JsciBWz1pgpIBcCJPE9S0LVg==} engines: {node: '>=12'} '@tanstack/store@0.8.0': @@ -1997,16 +2547,16 @@ packages: '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@25.0.10': - resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==} + '@types/node@25.2.0': + resolution: {integrity: sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==} '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.9': - resolution: {integrity: sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==} + '@types/react@19.2.11': + resolution: {integrity: sha512-tORuanb01iEzWvMGVGv2ZDhYZVeRMrw453DCSAIn/5yvcSVnMoUMTyf33nQJLahYEnv9xqrTNbgz4qY5EfSh0g==} '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -2025,8 +2575,8 @@ packages: engines: {node: '>=16.14.0'} hasBin: true - '@vitejs/plugin-react@5.1.2': - resolution: {integrity: sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==} + '@vitejs/plugin-react@5.1.3': + resolution: {integrity: sha512-NVUnA6gQCl8jfoYqKqQU5Clv0aPw14KkZYCsX6T9Lfu9slI0LOU10OTwFHS/WmptsMMpshNd/1tuWsHQ2Uk+cg==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -2105,8 +2655,42 @@ packages: typescript: optional: true - '@wagmi/core@3.3.1': - resolution: {integrity: sha512-0Q8VYnVNPHe/gZsvj+Zddt8VpmKoMHXoVd887svL21QGKXEIVYiV/8R3qMv0SyC7q+GbQ5x9xezB56u3S8bWAQ==} + '@wagmi/connectors@7.1.6': + resolution: {integrity: sha512-TKBTxSmiUh17tHdD7X1TQLtNIA4exuodPCwC0YuSaIRw8co1EivrUHjsHVsrJ7XKEsQhfp97ZRAVEssGcA4DPA==} + peerDependencies: + '@base-org/account': ^2.5.1 + '@coinbase/wallet-sdk': ^4.3.6 + '@gemini-wallet/core': ~0.3.1 + '@metamask/sdk': ~0.33.1 + '@safe-global/safe-apps-provider': ~0.18.6 + '@safe-global/safe-apps-sdk': ^9.1.0 + '@wagmi/core': 3.3.2 + '@walletconnect/ethereum-provider': ^2.21.1 + porto: ~0.2.35 + typescript: '>=5.7.3' + viem: 2.x + peerDependenciesMeta: + '@base-org/account': + optional: true + '@coinbase/wallet-sdk': + optional: true + '@gemini-wallet/core': + optional: true + '@metamask/sdk': + optional: true + '@safe-global/safe-apps-provider': + optional: true + '@safe-global/safe-apps-sdk': + optional: true + '@walletconnect/ethereum-provider': + optional: true + porto: + optional: true + typescript: + optional: true + + '@wagmi/core@3.3.2': + resolution: {integrity: sha512-e4aefdzEki657u7P6miuBijp0WKmtSsuY2/NT9e3zfJxr+QX5Edr5EcFF0Cg5OMMQ1y32x+g8ogMDppD9aX3kw==} peerDependencies: '@tanstack/query-core': '>=5.0.0' ox: '>=0.11.1' @@ -2232,6 +2816,9 @@ packages: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -2252,9 +2839,15 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + asn1.js@4.10.1: resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} @@ -2285,8 +2878,8 @@ packages: peerDependencies: axios: 0.x || 1.x - axios@1.13.3: - resolution: {integrity: sha512-ERT8kdX7DZjtUm7IitEyV7InTHAF42iJuMArIiDIV5YtPanJkgw4hw5Dyg9fh0mihdWNn1GKaeIWErfe56UQ1g==} + axios@1.13.4: + resolution: {integrity: sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==} babel-dead-code-elimination@1.0.12: resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} @@ -2306,8 +2899,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.11: - resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} + baseline-browser-mapping@2.9.19: + resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} hasBin: true bidi-js@1.0.3: @@ -2412,12 +3005,16 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001762: - resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==} + caniuse-lite@1.0.30001766: + resolution: {integrity: sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -2488,6 +3085,9 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -2498,6 +3098,19 @@ packages: constants-browserify@1.0.0: resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + conventional-changelog-angular@8.1.0: + resolution: {integrity: sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w==} + engines: {node: '>=18'} + + conventional-changelog-conventionalcommits@9.1.0: + resolution: {integrity: sha512-MnbEysR8wWa8dAEvbj5xcBgJKQlX/m0lhS8DsyAAWDHdfs2faDJxTgzRYlRYpXSe7UiKrIIlB4TrBKU9q9DgkA==} + engines: {node: '>=18'} + + conventional-commits-parser@6.2.1: + resolution: {integrity: sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA==} + engines: {node: '>=18'} + hasBin: true + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -2510,6 +3123,23 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cosmiconfig-typescript-loader@6.2.0: + resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} + engines: {node: '>=v18'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + create-ecdh@4.0.4: resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} @@ -2546,9 +3176,13 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - data-urls@6.0.0: - resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==} - engines: {node: '>=20'} + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} @@ -2630,15 +3264,19 @@ packages: resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} engines: {node: '>=10'} + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - effect@3.19.15: - resolution: {integrity: sha512-vzMmgfZKLcojmUjBdlQx+uaKryO7yULlRxjpDnHdnvcp1NPHxJyoM6IOXBLlzz2I/uPtZpGKavt5hBv7IvGZkA==} + effect@3.19.16: + resolution: {integrity: sha512-7+XC3vGrbAhCHd8LTFHvnZjRpZKZ8YHRZqJTkpNoxcJ2mCyNs2SwI+6VkV/ij8Y3YW7wfBN4EbU06/F5+m/wkQ==} - electron-to-chromium@1.5.267: - resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + electron-to-chromium@1.5.283: + resolution: {integrity: sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -2657,8 +3295,15 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} es-errors@1.3.0: @@ -2727,9 +3372,15 @@ packages: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-stable-stringify@1.0.0: resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -2807,10 +3458,19 @@ packages: get-tsconfig@4.13.0: resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + hasBin: true + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + goober@2.1.18: resolution: {integrity: sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==} peerDependencies: @@ -2873,6 +3533,11 @@ packages: humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + ico-endec@0.1.6: resolution: {integrity: sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==} @@ -2885,9 +3550,20 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -2895,6 +3571,9 @@ packages: resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} engines: {node: '>= 0.4'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.3.4: resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} @@ -2937,6 +3616,14 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -2958,8 +3645,8 @@ packages: isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isbot@5.1.33: - resolution: {integrity: sha512-P4Hgb5NqswjkI0J1CM6XKXon/sxKY1SuowE7Qx2hrBhIwICFyXy54mfgB5eMHXsbe/eStzzpbIGNOvGmz+dlKg==} + isbot@5.1.34: + resolution: {integrity: sha512-aCMIBSKd/XPRYdiCQTLC8QHH4YT8B3JUADu+7COgYIZPvkeoMcUHMRjZLM9/7V8fCj+l7FSREc1lOPNjzogo/A==} engines: {node: '>=18'} isomorphic-timers-promises@1.0.1: @@ -2991,8 +3678,12 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - jsdom@27.4.0: - resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdom@28.0.0: + resolution: {integrity: sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 @@ -3005,6 +3696,12 @@ packages: engines: {node: '>=6'} hasBin: true + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} @@ -3060,24 +3757,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.30.2: resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.30.2: resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.30.2: resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.30.2: resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} @@ -3095,6 +3796,9 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lit-element@4.2.2: resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} @@ -3112,11 +3816,29 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.clonedeep@4.5.0: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - lru-cache@11.2.4: - resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + + lru-cache@11.2.5: + resolution: {integrity: sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -3147,6 +3869,14 @@ packages: mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + miller-rabin@4.0.1: resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} hasBin: true @@ -3170,6 +3900,9 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + mipd@0.0.7: resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} peerDependencies: @@ -3320,10 +4053,18 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + parse-asn1@5.1.9: resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} engines: {node: '>= 0.10'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parse5@8.0.0: resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} @@ -3468,10 +4209,10 @@ packages: randomfill@1.0.4: resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} - react-dom@19.2.3: - resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: - react: ^19.2.3 + react: ^19.2.4 react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -3480,8 +4221,8 @@ packages: resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} engines: {node: '>=0.10.0'} - react@19.2.3: - resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} readable-stream@2.3.8: @@ -3524,6 +4265,14 @@ packages: reselect@5.1.1: resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -3536,8 +4285,8 @@ packages: resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} engines: {node: '>= 0.8'} - rollup@4.56.0: - resolution: {integrity: sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==} + rollup@4.57.1: + resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3582,22 +4331,12 @@ packages: engines: {node: '>=10'} hasBin: true - seroval-plugins@1.3.3: - resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} - engines: {node: '>=10'} - peerDependencies: - seroval: ^1.0 - seroval-plugins@1.5.0: resolution: {integrity: sha512-EAHqADIQondwRZIdeW2I636zgsODzoBDwb3PT/+7TLDWyw1Dy/Xv7iGUIEXXav7usHDE9HVhOU61irI3EnyyHA==} engines: {node: '>=10'} peerDependencies: seroval: ^1.0 - seroval@1.3.2: - resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} - engines: {node: '>=10'} - seroval@1.5.0: resolution: {integrity: sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw==} engines: {node: '>=10'} @@ -3657,8 +4396,8 @@ packages: slow-redact@0.3.2: resolution: {integrity: sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw==} - solid-js@1.9.10: - resolution: {integrity: sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==} + solid-js@1.9.11: + resolution: {integrity: sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==} sonic-boom@4.2.0: resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} @@ -3772,11 +4511,11 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} - tldts-core@7.0.19: - resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} + tldts-core@7.0.22: + resolution: {integrity: sha512-KgbTDC5wzlL6j/x6np6wCnDSMUq4kucHNm00KXPbfNzmllCmtmvtykJHfmgdHntwIeupW04y8s1N/43S1PkQDw==} - tldts@7.0.19: - resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} + tldts@7.0.22: + resolution: {integrity: sha512-nqpKFC53CgopKPjT6Wfb6tpIcZXHcI6G37hesvikhx0EmUGPkZrujRyAjgnmp1SHNgpQfKVanZ+KfpANFt2Hxw==} hasBin: true to-buffer@1.2.2: @@ -3819,6 +4558,40 @@ packages: tty-browserify@0.0.1: resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + turbo-darwin-64@2.8.3: + resolution: {integrity: sha512-4kXRLfcygLOeNcP6JquqRLmGB/ATjjfehiojL2dJkL7GFm3SPSXbq7oNj8UbD8XriYQ5hPaSuz59iF1ijPHkTw==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.8.3: + resolution: {integrity: sha512-xF7uCeC0UY0Hrv/tqax0BMbFlVP1J/aRyeGQPZT4NjvIPj8gSPDgFhfkfz06DhUwDg5NgMo04uiSkAWE8WB/QQ==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.8.3: + resolution: {integrity: sha512-vxMDXwaOjweW/4etY7BxrXCSkvtwh0PbwVafyfT1Ww659SedUxd5rM3V2ZCmbwG8NiCfY7d6VtxyHx3Wh1GoZA==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.8.3: + resolution: {integrity: sha512-mQX7uYBZFkuPLLlKaNe9IjR1JIef4YvY8f21xFocvttXvdPebnq3PK1Zjzl9A1zun2BEuWNUwQIL8lgvN9Pm3Q==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.8.3: + resolution: {integrity: sha512-YLGEfppGxZj3VWcNOVa08h6ISsVKiG85aCAWosOKNUjb6yErWEuydv6/qImRJUI+tDLvDvW7BxopAkujRnWCrw==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.8.3: + resolution: {integrity: sha512-afTUGKBRmOJU1smQSBnFGcbq0iabAPwh1uXu2BVk7BREg30/1gMnJh9DFEQTah+UD3n3ru8V55J83RQNFfqoyw==} + cpu: [arm64] + os: [win32] + + turbo@2.8.3: + resolution: {integrity: sha512-8Osxz5Tu/Dw2kb31EAY+nhq/YZ3wzmQSmYa1nIArqxgCAldxv9TPlrAiaBUDVnKA4aiPn0OFBD1ACcpc5VFOAQ==} + hasBin: true + tw-animate-css@1.4.0: resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} @@ -3855,11 +4628,15 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - undici-types@7.19.1: - resolution: {integrity: sha512-z2f4eae6/P3L9bogRUfLEZfRRxyrH4ssRq8s2/NOOgXEwwM5w0hsaj+mtDJPN7sBXQQNlagCzYUfjHywUiTETw==} + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + + undici@7.19.2: + resolution: {integrity: sha512-4VQSpGEGsWzk0VYxyB/wVX/Q7qf9t5znLRgs0dzszr9w9Fej/8RVNQ+S20vdXSAyra/bJ7ZQfGv6ZMj7UEbzSg==} + engines: {node: '>=20.18.1'} - undici@7.19.1: - resolution: {integrity: sha512-Gpq0iNm5M6cQWlyHQv9MV+uOj1jWk7LpkoE5vSp/7zjb4zMdAcUD+VL5y0nH4p9EbUklq00eVIIX/XcDHzu5xg==} + undici@7.20.0: + resolution: {integrity: sha512-MJZrkjyd7DeC+uPZh+5/YaMDxFiiEEaDgbUSVMXayofAkDWF1088CDo+2RPg7B1BuS1qf1vgNE7xqwPxE0DuSQ==} engines: {node: '>=20.18.1'} unplugin@2.3.11: @@ -3982,8 +4759,8 @@ packages: react: optional: true - viem@2.45.0: - resolution: {integrity: sha512-iVA9qrAgRdtpWa80lCZ6Jri6XzmLOwwA1wagX2HnKejKeliFLpON0KOdyfqvcy+gUpBVP59LBxP2aKiL3aj8fg==} + viem@2.45.1: + resolution: {integrity: sha512-LN6Pp7vSfv50LgwhkfSbIXftAM5J89lP9x8TeDa8QM7o41IxlHrDh0F9X+FfnCWtsz11pEVV5sn+yBUoOHNqYA==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -4035,8 +4812,8 @@ packages: yaml: optional: true - vitest-browser-react@2.0.4: - resolution: {integrity: sha512-FQq2z519Bwp/rANaQXU+ox7M4d0q/bTQkF2pgwRAehE+pqJ6myYOLp+P2Dy2kuk+K4IQJHMyijMCSQ1da/xW8w==} + vitest-browser-react@2.0.5: + resolution: {integrity: sha512-YODQX8mHTJCyKNVYTWJrLEYrUtw+QfLl78owgvuE7C5ydgmGBq6v5s4jK2w6wdPhIZsN9PpV1rQbmAevWJjO9g==} peerDependencies: '@types/react': ^18.0.0 || ^19.0.0 '@types/react-dom': ^18.0.0 || ^19.0.0 @@ -4090,8 +4867,8 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - wagmi@3.4.1: - resolution: {integrity: sha512-v6svxWxfIqV82lXNclOMn+h0SYCtXtxf0HWCwyjIJPZH1SR7yRqyQguWUDQtzvNSefFQEoCk+MVOX9nTR5d4Zw==} + wagmi@3.4.2: + resolution: {integrity: sha512-ZPZUquVh75NCHvb0qI+SBegUzcFHGIGtIKCL6gtHLcYHMcEMllqZGXtkIpc98IUq5Vq7Qey4FSG4ohTtMfQ/Yw==} peerDependencies: '@tanstack/react-query': '>=5.0.0' react: '>=18' @@ -4111,14 +4888,14 @@ packages: webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} - - whatwg-url@15.1.0: - resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} engines: {node: '>=20'} + whatwg-url@16.0.0: + resolution: {integrity: sha512-9CcxtEKsf53UFwkSUZjG+9vydAsFO4lFHBpJUtjBcoJOCJpKnSJNwCw813zrYJHpCJ7sgfbtOe0V5Ku7Pa1XMQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -4272,7 +5049,7 @@ packages: snapshots: - '@acemir/cssom@0.9.30': {} + '@acemir/cssom@0.9.31': {} '@adraffy/ens-normalize@1.11.1': {} @@ -4282,54 +5059,32 @@ snapshots: '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - lru-cache: 11.2.4 + lru-cache: 11.2.5 - '@asamuzakjp/dom-selector@6.7.6': + '@asamuzakjp/dom-selector@6.7.7': dependencies: '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 css-tree: 3.1.0 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.4 + lru-cache: 11.2.5 '@asamuzakjp/nwsapi@2.3.9': {} - '@babel/code-frame@7.27.1': + '@babel/code-frame@7.28.6': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/code-frame@7.28.6': + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.5': {} - '@babel/compat-data@7.28.6': {} - '@babel/core@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/core@7.28.6': dependencies: '@babel/code-frame': 7.28.6 @@ -4350,13 +5105,25 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.5': + '@babel/core@7.29.0': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color '@babel/generator@7.28.6': dependencies: @@ -4366,13 +5133,13 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.27.2': + '@babel/generator@7.29.1': dependencies: - '@babel/compat-data': 7.28.5 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 '@babel/helper-compilation-targets@7.28.6': dependencies: @@ -4384,13 +5151,6 @@ snapshots: '@babel/helper-globals@7.28.0': {} - '@babel/helper-module-imports@7.27.1': - dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-imports@7.28.6': dependencies: '@babel/traverse': 7.28.6 @@ -4398,26 +5158,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.6)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 + '@babel/core': 7.28.6 + '@babel/helper-module-imports': 7.28.6 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.6)': + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.29.0 '@babel/helper-module-imports': 7.28.6 '@babel/helper-validator-identifier': 7.28.5 '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-plugin-utils@7.28.6': {} '@babel/helper-string-parser@7.27.1': {} @@ -4426,24 +5184,19 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.4': - dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 - '@babel/helpers@7.28.6': dependencies: '@babel/template': 7.28.6 '@babel/types': 7.28.6 - '@babel/parser@7.28.5': - dependencies: - '@babel/types': 7.28.5 - '@babel/parser@7.28.6': dependencies: '@babel/types': 7.28.6 + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 @@ -4454,77 +5207,94 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/runtime@7.28.4': {} + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/runtime@7.28.6': {} - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.28.6 '@babel/parser': 7.28.6 '@babel/types': 7.28.6 - '@babel/traverse@7.28.5': + '@babel/traverse@7.28.6': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 + '@babel/code-frame': 7.28.6 + '@babel/generator': 7.28.6 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 + '@babel/parser': 7.28.6 + '@babel/template': 7.28.6 + '@babel/types': 7.28.6 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/traverse@7.28.6': + '@babel/traverse@7.29.0': dependencies: - '@babel/code-frame': 7.28.6 - '@babel/generator': 7.28.6 + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.6 + '@babel/parser': 7.29.0 '@babel/template': 7.28.6 - '@babel/types': 7.28.6 + '@babel/types': 7.29.0 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.28.5': + '@babel/types@7.28.6': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/types@7.28.6': + '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@base-org/account@2.4.0(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@base-org/account@2.4.0(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@coinbase/cdp-sdk': 1.44.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + preact: 10.24.2 + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.3(@types/react@19.2.11)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + optional: true + + '@base-org/account@2.4.0(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@coinbase/cdp-sdk': 1.43.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@coinbase/cdp-sdk': 1.44.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) preact: 10.24.2 - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - zustand: 5.0.3(@types/react@19.2.9)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.3(@types/react@19.2.11)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) transitivePeerDependencies: - '@types/react' - bufferutil @@ -4539,81 +5309,81 @@ snapshots: - zod optional: true - '@base-ui/react@1.1.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@base-ui/react@1.1.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@babel/runtime': 7.28.6 - '@base-ui/utils': 0.2.4(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@base-ui/utils': 0.2.4(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@floating-ui/react-dom': 2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@floating-ui/utils': 0.2.10 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) reselect: 5.1.1 tabbable: 6.4.0 - use-sync-external-store: 1.6.0(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.4) optionalDependencies: - '@types/react': 19.2.9 + '@types/react': 19.2.11 - '@base-ui/utils@0.2.4(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@base-ui/utils@0.2.4(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@babel/runtime': 7.28.6 '@floating-ui/utils': 0.2.10 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) reselect: 5.1.1 - use-sync-external-store: 1.6.0(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.4) optionalDependencies: - '@types/react': 19.2.9 + '@types/react': 19.2.11 - '@biomejs/biome@2.3.12': + '@biomejs/biome@2.3.14': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.3.12 - '@biomejs/cli-darwin-x64': 2.3.12 - '@biomejs/cli-linux-arm64': 2.3.12 - '@biomejs/cli-linux-arm64-musl': 2.3.12 - '@biomejs/cli-linux-x64': 2.3.12 - '@biomejs/cli-linux-x64-musl': 2.3.12 - '@biomejs/cli-win32-arm64': 2.3.12 - '@biomejs/cli-win32-x64': 2.3.12 + '@biomejs/cli-darwin-arm64': 2.3.14 + '@biomejs/cli-darwin-x64': 2.3.14 + '@biomejs/cli-linux-arm64': 2.3.14 + '@biomejs/cli-linux-arm64-musl': 2.3.14 + '@biomejs/cli-linux-x64': 2.3.14 + '@biomejs/cli-linux-x64-musl': 2.3.14 + '@biomejs/cli-win32-arm64': 2.3.14 + '@biomejs/cli-win32-x64': 2.3.14 - '@biomejs/cli-darwin-arm64@2.3.12': + '@biomejs/cli-darwin-arm64@2.3.14': optional: true - '@biomejs/cli-darwin-x64@2.3.12': + '@biomejs/cli-darwin-x64@2.3.14': optional: true - '@biomejs/cli-linux-arm64-musl@2.3.12': + '@biomejs/cli-linux-arm64-musl@2.3.14': optional: true - '@biomejs/cli-linux-arm64@2.3.12': + '@biomejs/cli-linux-arm64@2.3.14': optional: true - '@biomejs/cli-linux-x64-musl@2.3.12': + '@biomejs/cli-linux-x64-musl@2.3.14': optional: true - '@biomejs/cli-linux-x64@2.3.12': + '@biomejs/cli-linux-x64@2.3.14': optional: true - '@biomejs/cli-win32-arm64@2.3.12': + '@biomejs/cli-win32-arm64@2.3.14': optional: true - '@biomejs/cli-win32-x64@2.3.12': + '@biomejs/cli-win32-x64@2.3.14': optional: true '@canvas/image-data@1.1.0': {} - '@coinbase/cdp-sdk@1.43.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@coinbase/cdp-sdk@1.44.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: - '@solana-program/system': 0.10.0(@solana/kit@5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@solana-program/token': 0.9.0(@solana/kit@5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@solana/kit': 5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana-program/system': 0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana-program/token': 0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) abitype: 1.0.6(typescript@5.9.3)(zod@3.25.76) - axios: 1.13.3 - axios-retry: 4.5.0(axios@1.13.3) + axios: 1.13.4 + axios-retry: 4.5.0(axios@1.13.4) jose: 6.1.3 md5: 2.3.0 uncrypto: 0.1.3 - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -4624,6 +5394,115 @@ snapshots: - utf-8-validate optional: true + '@commitlint/cli@20.4.1(@types/node@25.2.0)(typescript@5.9.3)': + dependencies: + '@commitlint/format': 20.4.0 + '@commitlint/lint': 20.4.1 + '@commitlint/load': 20.4.0(@types/node@25.2.0)(typescript@5.9.3) + '@commitlint/read': 20.4.0 + '@commitlint/types': 20.4.0 + tinyexec: 1.0.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@20.4.1': + dependencies: + '@commitlint/types': 20.4.0 + conventional-changelog-conventionalcommits: 9.1.0 + + '@commitlint/config-validator@20.4.0': + dependencies: + '@commitlint/types': 20.4.0 + ajv: 8.17.1 + + '@commitlint/ensure@20.4.1': + dependencies: + '@commitlint/types': 20.4.0 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + + '@commitlint/execute-rule@20.0.0': {} + + '@commitlint/format@20.4.0': + dependencies: + '@commitlint/types': 20.4.0 + picocolors: 1.1.1 + + '@commitlint/is-ignored@20.4.1': + dependencies: + '@commitlint/types': 20.4.0 + semver: 7.7.3 + + '@commitlint/lint@20.4.1': + dependencies: + '@commitlint/is-ignored': 20.4.1 + '@commitlint/parse': 20.4.1 + '@commitlint/rules': 20.4.1 + '@commitlint/types': 20.4.0 + + '@commitlint/load@20.4.0(@types/node@25.2.0)(typescript@5.9.3)': + dependencies: + '@commitlint/config-validator': 20.4.0 + '@commitlint/execute-rule': 20.0.0 + '@commitlint/resolve-extends': 20.4.0 + '@commitlint/types': 20.4.0 + cosmiconfig: 9.0.0(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.2.0(@types/node@25.2.0)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3) + is-plain-obj: 4.1.0 + lodash.mergewith: 4.6.2 + picocolors: 1.1.1 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@20.4.0': {} + + '@commitlint/parse@20.4.1': + dependencies: + '@commitlint/types': 20.4.0 + conventional-changelog-angular: 8.1.0 + conventional-commits-parser: 6.2.1 + + '@commitlint/read@20.4.0': + dependencies: + '@commitlint/top-level': 20.4.0 + '@commitlint/types': 20.4.0 + git-raw-commits: 4.0.0 + minimist: 1.2.8 + tinyexec: 1.0.2 + + '@commitlint/resolve-extends@20.4.0': + dependencies: + '@commitlint/config-validator': 20.4.0 + '@commitlint/types': 20.4.0 + global-directory: 4.0.1 + import-meta-resolve: 4.2.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@commitlint/rules@20.4.1': + dependencies: + '@commitlint/ensure': 20.4.1 + '@commitlint/message': 20.4.0 + '@commitlint/to-lines': 20.0.0 + '@commitlint/types': 20.4.0 + + '@commitlint/to-lines@20.0.0': {} + + '@commitlint/top-level@20.4.0': + dependencies: + escalade: 3.2.0 + + '@commitlint/types@20.4.0': + dependencies: + conventional-commits-parser: 6.2.1 + picocolors: 1.1.1 + '@csstools/color-helpers@5.1.0': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': @@ -4642,100 +5521,100 @@ snapshots: dependencies: '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.22': {} + '@csstools/css-syntax-patches-for-csstree@1.0.26': {} '@csstools/css-tokenizer@3.0.4': {} - '@effect-atom/atom-react@0.4.6(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)(react@19.2.3)(scheduler@0.27.0)': + '@effect-atom/atom-react@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)(scheduler@0.27.0)': dependencies: - '@effect-atom/atom': 0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15) - effect: 3.19.15 - react: 19.2.3 + '@effect-atom/atom': 0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16) + effect: 3.19.16 + react: 19.2.4 scheduler: 0.27.0 transitivePeerDependencies: - '@effect/experimental' - '@effect/platform' - '@effect/rpc' - '@effect-atom/atom@0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)': + '@effect-atom/atom@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)': dependencies: - '@effect/experimental': 0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - '@effect/platform': 0.94.2(effect@3.19.15) - '@effect/rpc': 0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - effect: 3.19.15 + '@effect/experimental': 0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + '@effect/platform': 0.94.3(effect@3.19.16) + '@effect/rpc': 0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + effect: 3.19.16 - '@effect/cluster@0.56.1(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)': + '@effect/cluster@0.56.1(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)': dependencies: - '@effect/platform': 0.94.2(effect@3.19.15) - '@effect/rpc': 0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - '@effect/workflow': 0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15) - effect: 3.19.15 + '@effect/platform': 0.94.3(effect@3.19.16) + '@effect/rpc': 0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + '@effect/workflow': 0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16) + effect: 3.19.16 kubernetes-types: 1.30.0 - '@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15)': + '@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16)': dependencies: - '@effect/platform': 0.94.2(effect@3.19.15) - effect: 3.19.15 + '@effect/platform': 0.94.3(effect@3.19.16) + effect: 3.19.16 uuid: 11.1.0 - '@effect/language-service@0.72.0': {} + '@effect/language-service@0.73.0': {} - '@effect/platform-node-shared@0.57.1(@effect/cluster@0.56.1(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(bufferutil@4.1.0)(effect@3.19.15)(utf-8-validate@5.0.10)': + '@effect/platform-node-shared@0.57.1(@effect/cluster@0.56.1(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(bufferutil@4.1.0)(effect@3.19.16)(utf-8-validate@5.0.10)': dependencies: - '@effect/cluster': 0.56.1(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15) - '@effect/platform': 0.94.2(effect@3.19.15) - '@effect/rpc': 0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) + '@effect/cluster': 0.56.1(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16) + '@effect/platform': 0.94.3(effect@3.19.16) + '@effect/rpc': 0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) '@parcel/watcher': 2.5.6 - effect: 3.19.15 + effect: 3.19.16 multipasta: 0.2.7 ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform-node@0.104.1(@effect/cluster@0.56.1(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(bufferutil@4.1.0)(effect@3.19.15)(utf-8-validate@5.0.10)': + '@effect/platform-node@0.104.1(@effect/cluster@0.56.1(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(bufferutil@4.1.0)(effect@3.19.16)(utf-8-validate@5.0.10)': dependencies: - '@effect/cluster': 0.56.1(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15) - '@effect/platform': 0.94.2(effect@3.19.15) - '@effect/platform-node-shared': 0.57.1(@effect/cluster@0.56.1(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(bufferutil@4.1.0)(effect@3.19.15)(utf-8-validate@5.0.10) - '@effect/rpc': 0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - effect: 3.19.15 + '@effect/cluster': 0.56.1(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16) + '@effect/platform': 0.94.3(effect@3.19.16) + '@effect/platform-node-shared': 0.57.1(@effect/cluster@0.56.1(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(bufferutil@4.1.0)(effect@3.19.16)(utf-8-validate@5.0.10) + '@effect/rpc': 0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + effect: 3.19.16 mime: 3.0.0 - undici: 7.19.1 + undici: 7.19.2 ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform@0.94.2(effect@3.19.15)': + '@effect/platform@0.94.3(effect@3.19.16)': dependencies: - effect: 3.19.15 + effect: 3.19.16 find-my-way-ts: 0.1.6 msgpackr: 1.11.8 multipasta: 0.2.7 - '@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15)': + '@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16)': dependencies: - '@effect/platform': 0.94.2(effect@3.19.15) - effect: 3.19.15 + '@effect/platform': 0.94.3(effect@3.19.16) + effect: 3.19.16 msgpackr: 1.11.8 - '@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15)': + '@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16)': dependencies: - '@effect/experimental': 0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - '@effect/platform': 0.94.2(effect@3.19.15) - effect: 3.19.15 + '@effect/experimental': 0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + '@effect/platform': 0.94.3(effect@3.19.16) + effect: 3.19.16 uuid: 11.1.0 - '@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)': + '@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)': dependencies: - '@effect/experimental': 0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - '@effect/platform': 0.94.2(effect@3.19.15) - '@effect/rpc': 0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15) - effect: 3.19.15 + '@effect/experimental': 0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + '@effect/platform': 0.94.3(effect@3.19.16) + '@effect/rpc': 0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16) + effect: 3.19.16 '@emnapi/runtime@1.8.1': dependencies: @@ -4820,22 +5699,24 @@ snapshots: '@esbuild/win32-x64@0.27.2': optional: true - '@exodus/bytes@1.8.0': {} + '@exodus/bytes@1.11.0(@noble/hashes@1.8.0)': + optionalDependencies: + '@noble/hashes': 1.8.0 - '@floating-ui/core@1.7.3': + '@floating-ui/core@1.7.4': dependencies: '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.4': + '@floating-ui/dom@1.7.5': dependencies: - '@floating-ui/core': 1.7.3 + '@floating-ui/core': 1.7.4 '@floating-ui/utils': 0.2.10 - '@floating-ui/react-dom@2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@floating-ui/react-dom@2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@floating-ui/dom': 1.7.4 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + '@floating-ui/dom': 1.7.5 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) '@floating-ui/utils@0.2.10': {} @@ -4974,27 +5855,27 @@ snapshots: '@lit-labs/ssr-dom-shim@1.5.1': {} - '@lit/react@1.0.8(@types/react@19.2.9)': + '@lit/react@1.0.8(@types/react@19.2.11)': dependencies: - '@types/react': 19.2.9 + '@types/react': 19.2.11 optional: true '@lit/reactive-element@2.1.2': dependencies: '@lit-labs/ssr-dom-shim': 1.5.1 - '@lucas-barake/effect-form-react@0.14.0(@effect-atom/atom-react@0.4.6(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)(react@19.2.3)(scheduler@0.27.0))(@effect-atom/atom@0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)(react@19.2.3)': + '@lucas-barake/effect-form-react@0.18.0(@effect-atom/atom-react@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)(scheduler@0.27.0))(@effect-atom/atom@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)': dependencies: - '@effect-atom/atom': 0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15) - '@effect-atom/atom-react': 0.4.6(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)(react@19.2.3)(scheduler@0.27.0) - '@lucas-barake/effect-form': 0.14.0(@effect-atom/atom@0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15) - effect: 3.19.15 - react: 19.2.3 + '@effect-atom/atom': 0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16) + '@effect-atom/atom-react': 0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)(react@19.2.4)(scheduler@0.27.0) + '@lucas-barake/effect-form': 0.18.0(@effect-atom/atom@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16) + effect: 3.19.16 + react: 19.2.4 - '@lucas-barake/effect-form@0.14.0(@effect-atom/atom@0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15))(effect@3.19.15)': + '@lucas-barake/effect-form@0.18.0(@effect-atom/atom@0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16))(effect@3.19.16)': dependencies: - '@effect-atom/atom': 0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(@effect/platform@0.94.2(effect@3.19.15))(@effect/rpc@0.73.0(@effect/platform@0.94.2(effect@3.19.15))(effect@3.19.15))(effect@3.19.15) - effect: 3.19.15 + '@effect-atom/atom': 0.5.0(@effect/experimental@0.58.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(@effect/platform@0.94.3(effect@3.19.16))(@effect/rpc@0.73.0(@effect/platform@0.94.3(effect@3.19.16))(effect@3.19.16))(effect@3.19.16) + effect: 3.19.16 '@msgpack/msgpack@3.1.2': {} @@ -5111,22 +5992,78 @@ snapshots: dependencies: quansync: 1.0.0 - '@reown/appkit-adapter-wagmi@1.8.17(ddefe6db690784ffc0b4049c81f77f80)': + '@reown/appkit-adapter-wagmi@1.8.17(0a70dc20fe0c1a07d4cc186a53526855)': + dependencies: + '@reown/appkit': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.17 + '@reown/appkit-scaffold-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-utils': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@wagmi/core': 3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.2.11)(react@19.2.4) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 3.4.2(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.4))(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + optionalDependencies: + '@wagmi/connectors': 7.1.5(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@base-org/account' + - '@capacitor/preferences' + - '@coinbase/wallet-sdk' + - '@deno/kv' + - '@gemini-wallet/core' + - '@metamask/sdk' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@safe-global/safe-apps-provider' + - '@safe-global/safe-apps-sdk' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@walletconnect/ethereum-provider' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - porto + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-adapter-wagmi@1.8.17(588bfcce98e97548479faa9aa8a7b8c9)': dependencies: - '@reown/appkit': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.8.17 - '@reown/appkit-scaffold-ui': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.9)(react@19.2.3))(zod@3.25.76) - '@reown/appkit-utils': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.9)(react@19.2.3))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-utils': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@wagmi/core': 3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - valtio: 2.1.7(@types/react@19.2.9)(react@19.2.3) - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - wagmi: 3.4.1(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.3))(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + valtio: 2.1.7(@types/react@19.2.11)(react@19.2.4) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 3.4.2(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.4))(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) optionalDependencies: - '@wagmi/connectors': 7.1.5(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(typescript@5.9.3)(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/connectors': 7.1.5(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -5143,14 +6080,152 @@ snapshots: - '@netlify/blobs' - '@planetscale/database' - '@react-native-async-storage/async-storage' - - '@safe-global/safe-apps-provider' - - '@safe-global/safe-apps-sdk' + - '@safe-global/safe-apps-provider' + - '@safe-global/safe-apps-sdk' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@walletconnect/ethereum-provider' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - porto + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-common@1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-common@1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-controllers@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.2.11)(react@19.2.4) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-pay@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) + lit: 3.3.0 + valtio: 2.1.7(@types/react@19.2.11)(react@19.2.4) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-pay@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) + lit: 3.3.0 + valtio: 2.1.7(@types/react@19.2.11)(react@19.2.4) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' - '@types/react' - '@upstash/redis' - '@vercel/blob' - '@vercel/functions' - '@vercel/kv' - - '@walletconnect/ethereum-provider' - aws4fetch - bufferutil - db0 @@ -5159,7 +6234,6 @@ snapshots: - fastestsmallesttextencoderdecoder - immer - ioredis - - porto - react - typescript - uploadthing @@ -5167,35 +6241,19 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-common@1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - big.js: 6.2.2 - dayjs: 1.11.13 - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - zod - - '@reown/appkit-common@1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-polyfills@1.8.17': dependencies: - big.js: 6.2.2 - dayjs: 1.11.13 - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - zod + buffer: 6.0.3 - '@reown/appkit-controllers@1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - valtio: 2.1.7(@types/react@19.2.9)(react@19.2.3) - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + lit: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -5216,22 +6274,28 @@ snapshots: - aws4fetch - bufferutil - db0 + - debug - encoding + - fastestsmallesttextencoderdecoder + - immer - ioredis - react - typescript - uploadthing + - use-sync-external-store - utf-8-validate + - valtio - zod - '@reown/appkit-pay@1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.9)(react@19.2.3))(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) lit: 3.3.0 - valtio: 2.1.7(@types/react@19.2.9)(react@19.2.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -5262,21 +6326,17 @@ snapshots: - uploadthing - use-sync-external-store - utf-8-validate + - valtio - zod - '@reown/appkit-polyfills@1.8.17': - dependencies: - buffer: 6.0.3 - - '@reown/appkit-scaffold-ui@1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.9)(react@19.2.3))(zod@3.25.76)': + '@reown/appkit-ui@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: + '@phosphor-icons/webcomponents': 2.1.5 '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-pay': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.9)(react@19.2.3))(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) lit: 3.3.0 + qrcode: 1.5.3 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -5297,27 +6357,29 @@ snapshots: - aws4fetch - bufferutil - db0 - - debug - encoding - - fastestsmallesttextencoderdecoder - - immer - ioredis - react - typescript - uploadthing - - use-sync-external-store - utf-8-validate - - valtio - zod - '@reown/appkit-ui@1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-utils@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76)': dependencies: - '@phosphor-icons/webcomponents': 2.1.5 '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.17 '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - lit: 3.3.0 - qrcode: 1.5.3 + '@wallet-standard/wallet': 1.1.0 + '@walletconnect/logger': 3.0.2 + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.2.11)(react@19.2.4) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + '@base-org/account': 2.4.0(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -5338,27 +6400,31 @@ snapshots: - aws4fetch - bufferutil - db0 + - debug - encoding + - fastestsmallesttextencoderdecoder + - immer - ioredis - react - typescript - uploadthing + - use-sync-external-store - utf-8-validate - zod - '@reown/appkit-utils@1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.9)(react@19.2.3))(zod@3.25.76)': + '@reown/appkit-utils@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.8.17 '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@wallet-standard/wallet': 1.1.0 '@walletconnect/logger': 3.0.2 '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - valtio: 2.1.7(@types/react@19.2.9)(react@19.2.3) - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.2.11)(react@19.2.4) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: - '@base-org/account': 2.4.0(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76) + '@base-org/account': 2.4.0(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -5404,23 +6470,72 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.17 + '@reown/appkit-scaffold-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + bs58: 6.0.0 + semver: 7.7.2 + valtio: 2.1.7(@types/react@19.2.11)(react@19.2.4) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + '@lit/react': 1.0.8(@types/react@19.2.11) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit@1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-pay': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.8.17 - '@reown/appkit-scaffold-ui': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.9)(react@19.2.3))(zod@3.25.76) - '@reown/appkit-ui': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.8.17(@types/react@19.2.9)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.9)(react@19.2.3))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-ui': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17(@types/react@19.2.11)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.2.11)(react@19.2.4))(zod@3.25.76) '@reown/appkit-wallet': 1.8.17(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 semver: 7.7.2 - valtio: 2.1.7(@types/react@19.2.9)(react@19.2.3) - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.2.11)(react@19.2.4) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: - '@lit/react': 1.0.8(@types/react@19.2.9) + '@lit/react': 1.0.8(@types/react@19.2.11) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -5453,97 +6568,97 @@ snapshots: - utf-8-validate - zod - '@rolldown/pluginutils@1.0.0-beta.53': {} + '@rolldown/pluginutils@1.0.0-rc.2': {} - '@rollup/plugin-inject@5.0.5(rollup@4.56.0)': + '@rollup/plugin-inject@5.0.5(rollup@4.57.1)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.56.0) + '@rollup/pluginutils': 5.3.0(rollup@4.57.1) estree-walker: 2.0.2 magic-string: 0.30.21 optionalDependencies: - rollup: 4.56.0 + rollup: 4.57.1 - '@rollup/pluginutils@5.3.0(rollup@4.56.0)': + '@rollup/pluginutils@5.3.0(rollup@4.57.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.56.0 + rollup: 4.57.1 - '@rollup/rollup-android-arm-eabi@4.56.0': + '@rollup/rollup-android-arm-eabi@4.57.1': optional: true - '@rollup/rollup-android-arm64@4.56.0': + '@rollup/rollup-android-arm64@4.57.1': optional: true - '@rollup/rollup-darwin-arm64@4.56.0': + '@rollup/rollup-darwin-arm64@4.57.1': optional: true - '@rollup/rollup-darwin-x64@4.56.0': + '@rollup/rollup-darwin-x64@4.57.1': optional: true - '@rollup/rollup-freebsd-arm64@4.56.0': + '@rollup/rollup-freebsd-arm64@4.57.1': optional: true - '@rollup/rollup-freebsd-x64@4.56.0': + '@rollup/rollup-freebsd-x64@4.57.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.56.0': + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.56.0': + '@rollup/rollup-linux-arm-musleabihf@4.57.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.56.0': + '@rollup/rollup-linux-arm64-gnu@4.57.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.56.0': + '@rollup/rollup-linux-arm64-musl@4.57.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.56.0': + '@rollup/rollup-linux-loong64-gnu@4.57.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.56.0': + '@rollup/rollup-linux-loong64-musl@4.57.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.56.0': + '@rollup/rollup-linux-ppc64-gnu@4.57.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.56.0': + '@rollup/rollup-linux-ppc64-musl@4.57.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.56.0': + '@rollup/rollup-linux-riscv64-gnu@4.57.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.56.0': + '@rollup/rollup-linux-riscv64-musl@4.57.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.56.0': + '@rollup/rollup-linux-s390x-gnu@4.57.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.56.0': + '@rollup/rollup-linux-x64-gnu@4.57.1': optional: true - '@rollup/rollup-linux-x64-musl@4.56.0': + '@rollup/rollup-linux-x64-musl@4.57.1': optional: true - '@rollup/rollup-openbsd-x64@4.56.0': + '@rollup/rollup-openbsd-x64@4.57.1': optional: true - '@rollup/rollup-openharmony-arm64@4.56.0': + '@rollup/rollup-openharmony-arm64@4.57.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.56.0': + '@rollup/rollup-win32-arm64-msvc@4.57.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.56.0': + '@rollup/rollup-win32-ia32-msvc@4.57.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.56.0': + '@rollup/rollup-win32-x64-gnu@4.57.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.56.0': + '@rollup/rollup-win32-x64-msvc@4.57.1': optional: true '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': @@ -5560,7 +6675,7 @@ snapshots: '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript @@ -5575,7 +6690,7 @@ snapshots: '@scure/bip32@1.7.0': dependencies: - '@noble/curves': 1.9.1 + '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 @@ -5584,46 +6699,46 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 - '@solana-program/system@0.10.0(@solana/kit@5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))': + '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/kit': 5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) optional: true - '@solana-program/token@0.9.0(@solana/kit@5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))': + '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/kit': 5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) optional: true - '@solana/accounts@5.4.0(typescript@5.9.3)': + '@solana/accounts@5.5.1(typescript@5.9.3)': dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec': 5.4.0(typescript@5.9.3) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/addresses@5.4.0(typescript@5.9.3)': + '@solana/addresses@5.5.1(typescript@5.9.3)': dependencies: - '@solana/assertions': 5.4.0(typescript@5.9.3) - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/nominal-types': 5.4.0(typescript@5.9.3) + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/assertions@5.4.0(typescript@5.9.3)': + '@solana/assertions@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 optional: true @@ -5639,18 +6754,18 @@ snapshots: typescript: 5.9.3 optional: true - '@solana/codecs-core@5.4.0(typescript@5.9.3)': + '@solana/codecs-core@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 optional: true - '@solana/codecs-data-structures@5.4.0(typescript@5.9.3)': + '@solana/codecs-data-structures@5.5.1(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 optional: true @@ -5662,30 +6777,30 @@ snapshots: typescript: 5.9.3 optional: true - '@solana/codecs-numbers@5.4.0(typescript@5.9.3)': + '@solana/codecs-numbers@5.5.1(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 optional: true - '@solana/codecs-strings@5.4.0(typescript@5.9.3)': + '@solana/codecs-strings@5.5.1(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 optional: true - '@solana/codecs@5.4.0(typescript@5.9.3)': + '@solana/codecs@5.5.1(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-data-structures': 5.4.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/options': 5.4.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/options': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -5699,7 +6814,7 @@ snapshots: typescript: 5.9.3 optional: true - '@solana/errors@5.4.0(typescript@5.9.3)': + '@solana/errors@5.5.1(typescript@5.9.3)': dependencies: chalk: 5.6.2 commander: 14.0.2 @@ -5707,75 +6822,75 @@ snapshots: typescript: 5.9.3 optional: true - '@solana/fast-stable-stringify@5.4.0(typescript@5.9.3)': + '@solana/fast-stable-stringify@5.5.1(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 optional: true - '@solana/functional@5.4.0(typescript@5.9.3)': + '@solana/functional@5.5.1(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 optional: true - '@solana/instruction-plans@5.4.0(typescript@5.9.3)': + '@solana/instruction-plans@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/instructions': 5.4.0(typescript@5.9.3) - '@solana/keys': 5.4.0(typescript@5.9.3) - '@solana/promises': 5.4.0(typescript@5.9.3) - '@solana/transaction-messages': 5.4.0(typescript@5.9.3) - '@solana/transactions': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/instructions@5.4.0(typescript@5.9.3)': + '@solana/instructions@5.5.1(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 optional: true - '@solana/keys@5.4.0(typescript@5.9.3)': + '@solana/keys@5.5.1(typescript@5.9.3)': dependencies: - '@solana/assertions': 5.4.0(typescript@5.9.3) - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/nominal-types': 5.4.0(typescript@5.9.3) + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/kit@5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': - dependencies: - '@solana/accounts': 5.4.0(typescript@5.9.3) - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/codecs': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/functional': 5.4.0(typescript@5.9.3) - '@solana/instruction-plans': 5.4.0(typescript@5.9.3) - '@solana/instructions': 5.4.0(typescript@5.9.3) - '@solana/keys': 5.4.0(typescript@5.9.3) - '@solana/offchain-messages': 5.4.0(typescript@5.9.3) - '@solana/plugin-core': 5.4.0(typescript@5.9.3) - '@solana/programs': 5.4.0(typescript@5.9.3) - '@solana/rpc': 5.4.0(typescript@5.9.3) - '@solana/rpc-api': 5.4.0(typescript@5.9.3) - '@solana/rpc-parsed-types': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec-types': 5.4.0(typescript@5.9.3) - '@solana/rpc-subscriptions': 5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) - '@solana/signers': 5.4.0(typescript@5.9.3) - '@solana/sysvars': 5.4.0(typescript@5.9.3) - '@solana/transaction-confirmation': 5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/transaction-messages': 5.4.0(typescript@5.9.3) - '@solana/transactions': 5.4.0(typescript@5.9.3) + '@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instruction-plans': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/plugin-core': 5.5.1(typescript@5.9.3) + '@solana/programs': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/signers': 5.5.1(typescript@5.9.3) + '@solana/sysvars': 5.5.1(typescript@5.9.3) + '@solana/transaction-confirmation': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -5784,118 +6899,118 @@ snapshots: - utf-8-validate optional: true - '@solana/nominal-types@5.4.0(typescript@5.9.3)': + '@solana/nominal-types@5.5.1(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 optional: true - '@solana/offchain-messages@5.4.0(typescript@5.9.3)': + '@solana/offchain-messages@5.5.1(typescript@5.9.3)': dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-data-structures': 5.4.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/keys': 5.4.0(typescript@5.9.3) - '@solana/nominal-types': 5.4.0(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/options@5.4.0(typescript@5.9.3)': + '@solana/options@5.5.1(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-data-structures': 5.4.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/plugin-core@5.4.0(typescript@5.9.3)': + '@solana/plugin-core@5.5.1(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 optional: true - '@solana/programs@5.4.0(typescript@5.9.3)': + '@solana/programs@5.5.1(typescript@5.9.3)': dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/promises@5.4.0(typescript@5.9.3)': + '@solana/promises@5.5.1(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 optional: true - '@solana/rpc-api@5.4.0(typescript@5.9.3)': + '@solana/rpc-api@5.5.1(typescript@5.9.3)': dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/keys': 5.4.0(typescript@5.9.3) - '@solana/rpc-parsed-types': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec': 5.4.0(typescript@5.9.3) - '@solana/rpc-transformers': 5.4.0(typescript@5.9.3) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) - '@solana/transaction-messages': 5.4.0(typescript@5.9.3) - '@solana/transactions': 5.4.0(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/rpc-parsed-types@5.4.0(typescript@5.9.3)': + '@solana/rpc-parsed-types@5.5.1(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 optional: true - '@solana/rpc-spec-types@5.4.0(typescript@5.9.3)': + '@solana/rpc-spec-types@5.5.1(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 optional: true - '@solana/rpc-spec@5.4.0(typescript@5.9.3)': + '@solana/rpc-spec@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec-types': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 optional: true - '@solana/rpc-subscriptions-api@5.4.0(typescript@5.9.3)': + '@solana/rpc-subscriptions-api@5.5.1(typescript@5.9.3)': dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/keys': 5.4.0(typescript@5.9.3) - '@solana/rpc-subscriptions-spec': 5.4.0(typescript@5.9.3) - '@solana/rpc-transformers': 5.4.0(typescript@5.9.3) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) - '@solana/transaction-messages': 5.4.0(typescript@5.9.3) - '@solana/transactions': 5.4.0(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/rpc-subscriptions-channel-websocket@5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/functional': 5.4.0(typescript@5.9.3) - '@solana/rpc-subscriptions-spec': 5.4.0(typescript@5.9.3) - '@solana/subscribable': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.3 @@ -5904,29 +7019,29 @@ snapshots: - utf-8-validate optional: true - '@solana/rpc-subscriptions-spec@5.4.0(typescript@5.9.3)': + '@solana/rpc-subscriptions-spec@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/promises': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec-types': 5.4.0(typescript@5.9.3) - '@solana/subscribable': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 optional: true - '@solana/rpc-subscriptions@5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/rpc-subscriptions@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/fast-stable-stringify': 5.4.0(typescript@5.9.3) - '@solana/functional': 5.4.0(typescript@5.9.3) - '@solana/promises': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec-types': 5.4.0(typescript@5.9.3) - '@solana/rpc-subscriptions-api': 5.4.0(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-spec': 5.4.0(typescript@5.9.3) - '@solana/rpc-transformers': 5.4.0(typescript@5.9.3) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) - '@solana/subscribable': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-channel-websocket': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -5935,108 +7050,108 @@ snapshots: - utf-8-validate optional: true - '@solana/rpc-transformers@5.4.0(typescript@5.9.3)': + '@solana/rpc-transformers@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/functional': 5.4.0(typescript@5.9.3) - '@solana/nominal-types': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec-types': 5.4.0(typescript@5.9.3) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/rpc-transport-http@5.4.0(typescript@5.9.3)': + '@solana/rpc-transport-http@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec-types': 5.4.0(typescript@5.9.3) - undici-types: 7.19.1 + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + undici-types: 7.19.2 optionalDependencies: typescript: 5.9.3 optional: true - '@solana/rpc-types@5.4.0(typescript@5.9.3)': + '@solana/rpc-types@5.5.1(typescript@5.9.3)': dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/nominal-types': 5.4.0(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/rpc@5.4.0(typescript@5.9.3)': + '@solana/rpc@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/fast-stable-stringify': 5.4.0(typescript@5.9.3) - '@solana/functional': 5.4.0(typescript@5.9.3) - '@solana/rpc-api': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec': 5.4.0(typescript@5.9.3) - '@solana/rpc-spec-types': 5.4.0(typescript@5.9.3) - '@solana/rpc-transformers': 5.4.0(typescript@5.9.3) - '@solana/rpc-transport-http': 5.4.0(typescript@5.9.3) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-transport-http': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/signers@5.4.0(typescript@5.9.3)': + '@solana/signers@5.5.1(typescript@5.9.3)': dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/instructions': 5.4.0(typescript@5.9.3) - '@solana/keys': 5.4.0(typescript@5.9.3) - '@solana/nominal-types': 5.4.0(typescript@5.9.3) - '@solana/offchain-messages': 5.4.0(typescript@5.9.3) - '@solana/transaction-messages': 5.4.0(typescript@5.9.3) - '@solana/transactions': 5.4.0(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/subscribable@5.4.0(typescript@5.9.3)': + '@solana/subscribable@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.4.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 optional: true - '@solana/sysvars@5.4.0(typescript@5.9.3)': + '@solana/sysvars@5.5.1(typescript@5.9.3)': dependencies: - '@solana/accounts': 5.4.0(typescript@5.9.3) - '@solana/codecs': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/transaction-confirmation@5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/transaction-confirmation@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/keys': 5.4.0(typescript@5.9.3) - '@solana/promises': 5.4.0(typescript@5.9.3) - '@solana/rpc': 5.4.0(typescript@5.9.3) - '@solana/rpc-subscriptions': 5.4.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) - '@solana/transaction-messages': 5.4.0(typescript@5.9.3) - '@solana/transactions': 5.4.0(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6045,37 +7160,37 @@ snapshots: - utf-8-validate optional: true - '@solana/transaction-messages@5.4.0(typescript@5.9.3)': + '@solana/transaction-messages@5.5.1(typescript@5.9.3)': dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-data-structures': 5.4.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/functional': 5.4.0(typescript@5.9.3) - '@solana/instructions': 5.4.0(typescript@5.9.3) - '@solana/nominal-types': 5.4.0(typescript@5.9.3) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder optional: true - '@solana/transactions@5.4.0(typescript@5.9.3)': - dependencies: - '@solana/addresses': 5.4.0(typescript@5.9.3) - '@solana/codecs-core': 5.4.0(typescript@5.9.3) - '@solana/codecs-data-structures': 5.4.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.4.0(typescript@5.9.3) - '@solana/codecs-strings': 5.4.0(typescript@5.9.3) - '@solana/errors': 5.4.0(typescript@5.9.3) - '@solana/functional': 5.4.0(typescript@5.9.3) - '@solana/instructions': 5.4.0(typescript@5.9.3) - '@solana/keys': 5.4.0(typescript@5.9.3) - '@solana/nominal-types': 5.4.0(typescript@5.9.3) - '@solana/rpc-types': 5.4.0(typescript@5.9.3) - '@solana/transaction-messages': 5.4.0(typescript@5.9.3) + '@solana/transactions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6086,7 +7201,7 @@ snapshots: dependencies: '@babel/runtime': 7.28.6 '@noble/curves': 1.9.7 - '@noble/hashes': 1.4.0 + '@noble/hashes': 1.8.0 '@solana/buffer-layout': 4.0.1 '@solana/codecs-numbers': 2.3.0(typescript@5.9.3) agentkeepalive: 4.6.0 @@ -6106,39 +7221,39 @@ snapshots: - utf-8-validate optional: true - '@solid-primitives/event-listener@2.4.3(solid-js@1.9.10)': + '@solid-primitives/event-listener@2.4.3(solid-js@1.9.11)': dependencies: - '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) - solid-js: 1.9.10 + '@solid-primitives/utils': 6.3.2(solid-js@1.9.11) + solid-js: 1.9.11 - '@solid-primitives/keyboard@1.3.3(solid-js@1.9.10)': + '@solid-primitives/keyboard@1.3.3(solid-js@1.9.11)': dependencies: - '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10) - '@solid-primitives/rootless': 1.5.2(solid-js@1.9.10) - '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) - solid-js: 1.9.10 + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.11) + '@solid-primitives/rootless': 1.5.2(solid-js@1.9.11) + '@solid-primitives/utils': 6.3.2(solid-js@1.9.11) + solid-js: 1.9.11 - '@solid-primitives/resize-observer@2.1.3(solid-js@1.9.10)': + '@solid-primitives/resize-observer@2.1.3(solid-js@1.9.11)': dependencies: - '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10) - '@solid-primitives/rootless': 1.5.2(solid-js@1.9.10) - '@solid-primitives/static-store': 0.1.2(solid-js@1.9.10) - '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) - solid-js: 1.9.10 + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.11) + '@solid-primitives/rootless': 1.5.2(solid-js@1.9.11) + '@solid-primitives/static-store': 0.1.2(solid-js@1.9.11) + '@solid-primitives/utils': 6.3.2(solid-js@1.9.11) + solid-js: 1.9.11 - '@solid-primitives/rootless@1.5.2(solid-js@1.9.10)': + '@solid-primitives/rootless@1.5.2(solid-js@1.9.11)': dependencies: - '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) - solid-js: 1.9.10 + '@solid-primitives/utils': 6.3.2(solid-js@1.9.11) + solid-js: 1.9.11 - '@solid-primitives/static-store@0.1.2(solid-js@1.9.10)': + '@solid-primitives/static-store@0.1.2(solid-js@1.9.11)': dependencies: - '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) - solid-js: 1.9.10 + '@solid-primitives/utils': 6.3.2(solid-js@1.9.11) + solid-js: 1.9.11 - '@solid-primitives/utils@6.3.2(solid-js@1.9.10)': + '@solid-primitives/utils@6.3.2(solid-js@1.9.11)': dependencies: - solid-js: 1.9.10 + solid-js: 1.9.11 '@stacks/common@6.16.0': dependencies: @@ -6233,12 +7348,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 - '@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + '@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@tailwindcss/node': 4.1.18 '@tailwindcss/oxide': 4.1.18 tailwindcss: 4.1.18 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) '@tanstack/devtools-client@0.0.5': dependencies: @@ -6253,43 +7368,43 @@ snapshots: '@tanstack/devtools-event-client@0.4.0': {} - '@tanstack/devtools-ui@0.4.4(csstype@3.2.3)(solid-js@1.9.10)': + '@tanstack/devtools-ui@0.4.4(csstype@3.2.3)(solid-js@1.9.11)': dependencies: clsx: 2.1.1 goober: 2.1.18(csstype@3.2.3) - solid-js: 1.9.10 + solid-js: 1.9.11 transitivePeerDependencies: - csstype - '@tanstack/devtools-vite@0.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/devtools-vite@0.5.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - '@babel/core': 7.28.6 - '@babel/generator': 7.28.6 - '@babel/parser': 7.28.6 - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.0 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 '@tanstack/devtools-client': 0.0.5 '@tanstack/devtools-event-bus': 0.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) chalk: 5.6.2 launch-editor: 2.12.0 picomatch: 4.0.3 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@tanstack/devtools@0.10.3(bufferutil@4.1.0)(csstype@3.2.3)(solid-js@1.9.10)(utf-8-validate@5.0.10)': + '@tanstack/devtools@0.10.5(bufferutil@4.1.0)(csstype@3.2.3)(solid-js@1.9.11)(utf-8-validate@5.0.10)': dependencies: - '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10) - '@solid-primitives/keyboard': 1.3.3(solid-js@1.9.10) - '@solid-primitives/resize-observer': 2.1.3(solid-js@1.9.10) + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.11) + '@solid-primitives/keyboard': 1.3.3(solid-js@1.9.11) + '@solid-primitives/resize-observer': 2.1.3(solid-js@1.9.11) '@tanstack/devtools-client': 0.0.5 '@tanstack/devtools-event-bus': 0.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@tanstack/devtools-ui': 0.4.4(csstype@3.2.3)(solid-js@1.9.10) + '@tanstack/devtools-ui': 0.4.4(csstype@3.2.3)(solid-js@1.9.11) clsx: 2.1.1 goober: 2.1.18(csstype@3.2.3) - solid-js: 1.9.10 + solid-js: 1.9.11 transitivePeerDependencies: - bufferutil - csstype @@ -6299,78 +7414,68 @@ snapshots: '@tanstack/query-core@5.90.20': {} - '@tanstack/react-devtools@0.9.2(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(bufferutil@4.1.0)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.10)(utf-8-validate@5.0.10)': + '@tanstack/react-devtools@0.9.4(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(bufferutil@4.1.0)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(solid-js@1.9.11)(utf-8-validate@5.0.10)': dependencies: - '@tanstack/devtools': 0.10.3(bufferutil@4.1.0)(csstype@3.2.3)(solid-js@1.9.10)(utf-8-validate@5.0.10) - '@types/react': 19.2.9 - '@types/react-dom': 19.2.3(@types/react@19.2.9) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + '@tanstack/devtools': 0.10.5(bufferutil@4.1.0)(csstype@3.2.3)(solid-js@1.9.11)(utf-8-validate@5.0.10) + '@types/react': 19.2.11 + '@types/react-dom': 19.2.3(@types/react@19.2.11) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) transitivePeerDependencies: - bufferutil - csstype - solid-js - utf-8-validate - '@tanstack/react-query@5.90.20(react@19.2.3)': + '@tanstack/react-query@5.90.20(react@19.2.4)': dependencies: '@tanstack/query-core': 5.90.20 - react: 19.2.3 + react: 19.2.4 - '@tanstack/react-router-devtools@1.157.8(@tanstack/react-router@1.157.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.157.15)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@tanstack/react-router-devtools@1.158.0(@tanstack/react-router@1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.158.0)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@tanstack/react-router': 1.157.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/router-devtools-core': 1.157.8(@tanstack/router-core@1.157.15)(csstype@3.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + '@tanstack/react-router': 1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/router-devtools-core': 1.158.0(@tanstack/router-core@1.158.0)(csstype@3.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) optionalDependencies: - '@tanstack/router-core': 1.157.15 + '@tanstack/router-core': 1.158.0 transitivePeerDependencies: - csstype - '@tanstack/react-router@1.157.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@tanstack/react-router@1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@tanstack/history': 1.154.14 - '@tanstack/react-store': 0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/router-core': 1.157.8 - isbot: 5.1.33 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + '@tanstack/react-store': 0.8.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/router-core': 1.158.0 + isbot: 5.1.34 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-store@0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@tanstack/react-store@0.8.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@tanstack/store': 0.8.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - use-sync-external-store: 1.6.0(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.4) - '@tanstack/react-virtual@3.13.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@tanstack/react-virtual@3.13.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@tanstack/virtual-core': 3.13.18 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) - '@tanstack/router-cli@1.157.15': + '@tanstack/router-cli@1.158.0': dependencies: - '@tanstack/router-generator': 1.157.15 + '@tanstack/router-generator': 1.158.0 chokidar: 3.6.0 yargs: 17.7.2 transitivePeerDependencies: - supports-color - '@tanstack/router-core@1.157.15': - dependencies: - '@tanstack/history': 1.154.14 - '@tanstack/store': 0.8.0 - cookie-es: 2.0.0 - seroval: 1.5.0 - seroval-plugins: 1.5.0(seroval@1.5.0) - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - - '@tanstack/router-core@1.157.8': + '@tanstack/router-core@1.158.0': dependencies: '@tanstack/history': 1.154.14 '@tanstack/store': 0.8.0 @@ -6380,32 +7485,19 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/router-devtools-core@1.157.8(@tanstack/router-core@1.157.15)(csstype@3.2.3)': + '@tanstack/router-devtools-core@1.158.0(@tanstack/router-core@1.158.0)(csstype@3.2.3)': dependencies: - '@tanstack/router-core': 1.157.15 + '@tanstack/router-core': 1.158.0 clsx: 2.1.1 goober: 2.1.18(csstype@3.2.3) tiny-invariant: 1.3.3 optionalDependencies: csstype: 3.2.3 - '@tanstack/router-generator@1.157.15': - dependencies: - '@tanstack/router-core': 1.157.15 - '@tanstack/router-utils': 1.154.7 - '@tanstack/virtual-file-routes': 1.154.7 - prettier: 3.8.1 - recast: 0.23.11 - source-map: 0.7.6 - tsx: 4.21.0 - zod: 3.25.76 - transitivePeerDependencies: - - supports-color - - '@tanstack/router-generator@1.157.8': + '@tanstack/router-generator@1.158.0': dependencies: - '@tanstack/router-core': 1.157.8 - '@tanstack/router-utils': 1.154.7 + '@tanstack/router-core': 1.158.0 + '@tanstack/router-utils': 1.158.0 '@tanstack/virtual-file-routes': 1.154.7 prettier: 3.8.1 recast: 0.23.11 @@ -6415,7 +7507,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.157.8(@tanstack/react-router@1.157.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/router-plugin@1.158.0(@tanstack/react-router@1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.6 '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.28.6) @@ -6423,26 +7515,27 @@ snapshots: '@babel/template': 7.28.6 '@babel/traverse': 7.28.6 '@babel/types': 7.28.6 - '@tanstack/router-core': 1.157.8 - '@tanstack/router-generator': 1.157.8 - '@tanstack/router-utils': 1.154.7 + '@tanstack/router-core': 1.158.0 + '@tanstack/router-generator': 1.158.0 + '@tanstack/router-utils': 1.158.0 '@tanstack/virtual-file-routes': 1.154.7 - babel-dead-code-elimination: 1.0.12 chokidar: 3.6.0 unplugin: 2.3.11 zod: 3.25.76 optionalDependencies: - '@tanstack/react-router': 1.157.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + '@tanstack/react-router': 1.158.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + vite: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@tanstack/router-utils@1.154.7': + '@tanstack/router-utils@1.158.0': dependencies: '@babel/core': 7.28.6 '@babel/generator': 7.28.6 '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 ansis: 4.2.0 + babel-dead-code-elimination: 1.0.12 diff: 8.0.3 pathe: 2.0.3 tinyglobby: 0.2.15 @@ -6457,8 +7550,8 @@ snapshots: '@testing-library/dom@10.4.1': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/runtime': 7.28.4 + '@babel/code-frame': 7.28.6 + '@babel/runtime': 7.28.6 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -6466,15 +7559,15 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@babel/runtime': 7.28.6 '@testing-library/dom': 10.4.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) optionalDependencies: - '@types/react': 19.2.9 - '@types/react-dom': 19.2.3(@types/react@19.2.9) + '@types/react': 19.2.11 + '@types/react-dom': 19.2.3(@types/react@19.2.11) '@tim-smart/openapi-gen@0.4.13(patch_hash=36720013d0f70c201ce8f233e38a7b93fc9fce74a17f9bc4293c3cf891b4fdc6)': dependencies: @@ -6498,28 +7591,28 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 '@types/bn.js@5.2.0': dependencies: - '@types/node': 25.0.10 + '@types/node': 25.2.0 '@types/chai@5.2.3': dependencies: @@ -6528,7 +7621,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 25.0.10 + '@types/node': 25.2.0 optional: true '@types/deep-eql@4.0.2': {} @@ -6542,15 +7635,15 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@25.0.10': + '@types/node@25.2.0': dependencies: undici-types: 7.16.0 - '@types/react-dom@19.2.3(@types/react@19.2.9)': + '@types/react-dom@19.2.3(@types/react@19.2.11)': dependencies: - '@types/react': 19.2.9 + '@types/react': 19.2.11 - '@types/react@19.2.9': + '@types/react@19.2.11': dependencies: csstype: 3.2.3 @@ -6561,12 +7654,12 @@ snapshots: '@types/ws@7.4.7': dependencies: - '@types/node': 25.0.10 + '@types/node': 25.2.0 optional: true '@types/ws@8.18.1': dependencies: - '@types/node': 25.0.10 + '@types/node': 25.2.0 optional: true '@vite-pwa/assets-generator@1.0.2': @@ -6578,41 +7671,41 @@ snapshots: sharp-ico: 0.1.5 unconfig: 7.4.2 - '@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + '@vitejs/plugin-react@5.1.3(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) - '@rolldown/pluginutils': 1.0.0-beta.53 + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-rc.2 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@vitest/browser-playwright@4.0.18(bufferutil@4.1.0)(playwright@1.58.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18)': + '@vitest/browser-playwright@4.0.18(bufferutil@4.1.0)(playwright@1.58.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18)': dependencies: - '@vitest/browser': 4.0.18(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/browser': 4.0.18(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) playwright: 1.58.0 tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.0.18(@types/node@25.2.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.0.18(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18)': + '@vitest/browser@4.0.18(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18)': dependencies: - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/utils': 4.0.18 magic-string: 0.30.21 pixelmatch: 7.1.0 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.0.18(@types/node@25.2.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil @@ -6629,13 +7722,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@4.0.18': dependencies: @@ -6659,21 +7752,57 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 - '@wagmi/connectors@7.1.5(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(typescript@5.9.3)(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@wagmi/connectors@7.1.5(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + '@wagmi/core': 3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + typescript: 5.9.3 + optional: true + + '@wagmi/connectors@7.1.5(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + '@wagmi/core': 3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + typescript: 5.9.3 + optional: true + + '@wagmi/connectors@7.1.6(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: - '@wagmi/core': 3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@wagmi/core': 3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) typescript: 5.9.3 - '@wagmi/core@3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@wagmi/core@3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.9.3) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.0(@types/react@19.2.11)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)) + optionalDependencies: + '@tanstack/query-core': 5.90.20 + ox: 0.11.3(typescript@5.9.3)(zod@3.25.76) + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + + '@wagmi/core@3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.9.3) - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - zustand: 5.0.0(@types/react@19.2.9)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.0(@types/react@19.2.11)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) optionalDependencies: '@tanstack/query-core': 5.90.20 ox: 0.11.3(typescript@5.9.3)(zod@3.25.76) @@ -7019,6 +8148,13 @@ snapshots: humanize-ms: 1.2.1 optional: true + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-regex@5.0.1: {} ansi-styles@4.3.0: @@ -7034,10 +8170,14 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + argparse@2.0.1: {} + aria-query@5.3.0: dependencies: dequal: 2.0.3 + array-ify@1.0.0: {} + asn1.js@4.10.1: dependencies: bn.js: 4.12.2 @@ -7067,13 +8207,13 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axios-retry@4.5.0(axios@1.13.3): + axios-retry@4.5.0(axios@1.13.4): dependencies: - axios: 1.13.3 + axios: 1.13.4 is-retry-allowed: 2.2.0 optional: true - axios@1.13.3: + axios@1.13.4: dependencies: follow-redirects: 1.15.11 form-data: 4.0.5 @@ -7093,7 +8233,7 @@ snapshots: babel-plugin-react-compiler@1.0.0: dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 base-x@3.0.11: dependencies: @@ -7106,7 +8246,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.9.11: {} + baseline-browser-mapping@2.9.19: {} bidi-js@1.0.3: dependencies: @@ -7187,9 +8327,9 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.9.11 - caniuse-lite: 1.0.30001762 - electron-to-chromium: 1.5.267 + baseline-browser-mapping: 2.9.19 + caniuse-lite: 1.0.30001766 + electron-to-chromium: 1.5.283 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -7245,9 +8385,11 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + callsites@3.1.0: {} + camelcase@5.3.1: {} - caniuse-lite@1.0.30001762: {} + caniuse-lite@1.0.30001766: {} chai@6.2.2: {} @@ -7328,12 +8470,29 @@ snapshots: commander@2.20.3: optional: true + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + consola@3.4.2: {} console-browserify@1.2.0: {} constants-browserify@1.0.0: {} + conventional-changelog-angular@8.1.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@9.1.0: + dependencies: + compare-func: 2.0.0 + + conventional-commits-parser@6.2.1: + dependencies: + meow: 13.2.0 + convert-source-map@2.0.0: {} cookie-es@1.2.2: {} @@ -7342,6 +8501,22 @@ snapshots: core-util-is@1.0.3: {} + cosmiconfig-typescript-loader@6.2.0(@types/node@25.2.0)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3): + dependencies: + '@types/node': 25.2.0 + cosmiconfig: 9.0.0(typescript@5.9.3) + jiti: 2.6.1 + typescript: 5.9.3 + + cosmiconfig@9.0.0(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + create-ecdh@4.0.4: dependencies: bn.js: 4.12.2 @@ -7402,16 +8577,20 @@ snapshots: cssstyle@5.3.7: dependencies: '@asamuzakjp/css-color': 4.1.1 - '@csstools/css-syntax-patches-for-csstree': 1.0.22 + '@csstools/css-syntax-patches-for-csstree': 1.0.26 css-tree: 3.1.0 - lru-cache: 11.2.4 + lru-cache: 11.2.5 csstype@3.2.3: {} - data-urls@6.0.0: + dargs@8.1.0: {} + + data-urls@7.0.0(@noble/hashes@1.8.0): dependencies: - whatwg-mimetype: 4.0.0 - whatwg-url: 15.1.0 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.0(@noble/hashes@1.8.0) + transitivePeerDependencies: + - '@noble/hashes' dayjs@1.11.13: {} @@ -7481,18 +8660,22 @@ snapshots: domain-browser@4.22.0: {} + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 - effect@3.19.15: + effect@3.19.16: dependencies: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 - electron-to-chromium@1.5.267: {} + electron-to-chromium@1.5.283: {} elliptic@6.6.1: dependencies: @@ -7515,6 +8698,12 @@ snapshots: entities@6.0.1: {} + env-paths@2.2.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -7600,9 +8789,13 @@ snapshots: dependencies: pure-rand: 6.1.0 + fast-deep-equal@3.1.3: {} + fast-stable-stringify@1.0.0: optional: true + fast-uri@3.1.0: {} + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -7675,10 +8868,20 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + git-raw-commits@4.0.0: + dependencies: + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + goober@2.1.18(csstype@3.2.3): dependencies: csstype: 3.2.3 @@ -7736,11 +8939,11 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - html-encoding-sniffer@6.0.0: + html-encoding-sniffer@6.0.0(@noble/hashes@1.8.0): dependencies: - '@exodus/bytes': 1.8.0 + '@exodus/bytes': 1.11.0(@noble/hashes@1.8.0) transitivePeerDependencies: - - '@exodus/crypto' + - '@noble/hashes' http-proxy-agent@7.0.2: dependencies: @@ -7763,6 +8966,8 @@ snapshots: ms: 2.1.3 optional: true + husky@9.1.7: {} + ico-endec@0.1.6: {} idb-keyval@6.2.1: @@ -7772,8 +8977,17 @@ snapshots: ieee754@1.2.1: {} + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.2.0: {} + inherits@2.0.4: {} + ini@4.1.1: {} + iron-webcrypto@1.2.1: {} is-arguments@1.2.0: @@ -7781,6 +8995,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-arrayish@0.2.1: {} + is-arrayish@0.3.4: {} is-binary-path@2.1.0: @@ -7819,6 +9035,10 @@ snapshots: is-number@7.0.0: {} + is-obj@2.0.0: {} + + is-plain-obj@4.1.0: {} + is-potential-custom-element-name@1.0.1: {} is-regex@1.2.1: @@ -7839,7 +9059,7 @@ snapshots: isarray@2.0.5: {} - isbot@5.1.33: {} + isbot@5.1.34: {} isomorphic-timers-promises@1.0.1: {} @@ -7878,15 +9098,19 @@ snapshots: js-tokens@4.0.0: {} - jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsdom@28.0.0(@noble/hashes@1.8.0): dependencies: - '@acemir/cssom': 0.9.30 - '@asamuzakjp/dom-selector': 6.7.6 - '@exodus/bytes': 1.8.0 + '@acemir/cssom': 0.9.31 + '@asamuzakjp/dom-selector': 6.7.7 + '@exodus/bytes': 1.11.0(@noble/hashes@1.8.0) cssstyle: 5.3.7 - data-urls: 6.0.0 + data-urls: 7.0.0(@noble/hashes@1.8.0) decimal.js: 10.6.0 - html-encoding-sniffer: 6.0.0 + html-encoding-sniffer: 6.0.0(@noble/hashes@1.8.0) http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 @@ -7894,20 +9118,22 @@ snapshots: saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.0 + undici: 7.20.0 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 15.1.0 - ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.0(@noble/hashes@1.8.0) xml-name-validator: 5.0.0 transitivePeerDependencies: - - '@exodus/crypto' - - bufferutil + - '@noble/hashes' - supports-color - - utf-8-validate jsesc@3.1.0: {} + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@1.0.0: {} + json-stringify-safe@5.0.1: optional: true @@ -7973,6 +9199,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 + lines-and-columns@1.2.4: {} + lit-element@4.2.2: dependencies: '@lit-labs/ssr-dom-shim': 1.5.1 @@ -7997,17 +9225,29 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.camelcase@4.3.0: {} + lodash.clonedeep@4.5.0: {} - lru-cache@11.2.4: {} + lodash.kebabcase@4.1.1: {} + + lodash.mergewith@4.6.2: {} + + lodash.snakecase@4.1.1: {} + + lodash.startcase@4.4.0: {} + + lodash.upperfirst@4.3.1: {} + + lru-cache@11.2.5: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lucide-react@0.563.0(react@19.2.3): + lucide-react@0.563.0(react@19.2.4): dependencies: - react: 19.2.3 + react: 19.2.4 lz-string@1.5.0: {} @@ -8019,7 +9259,7 @@ snapshots: md5.js@1.3.5: dependencies: - hash-base: 3.0.5 + hash-base: 3.1.2 inherits: 2.0.4 safe-buffer: 5.2.1 @@ -8032,6 +9272,10 @@ snapshots: mdn-data@2.12.2: {} + meow@12.1.1: {} + + meow@13.2.0: {} + miller-rabin@4.0.1: dependencies: bn.js: 4.12.2 @@ -8051,6 +9295,8 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} + minimist@1.2.8: {} + mipd@0.0.7(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -8249,6 +9495,10 @@ snapshots: pako@1.0.11: {} + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + parse-asn1@5.1.9: dependencies: asn1.js: 4.10.1 @@ -8257,6 +9507,13 @@ snapshots: pbkdf2: 3.1.5 safe-buffer: 5.2.1 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parse5@8.0.0: dependencies: entities: 6.0.1 @@ -8397,16 +9654,16 @@ snapshots: randombytes: 2.1.0 safe-buffer: 5.2.1 - react-dom@19.2.3(react@19.2.3): + react-dom@19.2.4(react@19.2.4): dependencies: - react: 19.2.3 + react: 19.2.4 scheduler: 0.27.0 react-is@17.0.2: {} react-refresh@0.18.0: {} - react@19.2.3: {} + react@19.2.4: {} readable-stream@2.3.8: dependencies: @@ -8450,6 +9707,10 @@ snapshots: reselect@5.1.1: {} + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} resolve@1.22.11: @@ -8463,35 +9724,35 @@ snapshots: hash-base: 3.1.2 inherits: 2.0.4 - rollup@4.56.0: + rollup@4.57.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.56.0 - '@rollup/rollup-android-arm64': 4.56.0 - '@rollup/rollup-darwin-arm64': 4.56.0 - '@rollup/rollup-darwin-x64': 4.56.0 - '@rollup/rollup-freebsd-arm64': 4.56.0 - '@rollup/rollup-freebsd-x64': 4.56.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.56.0 - '@rollup/rollup-linux-arm-musleabihf': 4.56.0 - '@rollup/rollup-linux-arm64-gnu': 4.56.0 - '@rollup/rollup-linux-arm64-musl': 4.56.0 - '@rollup/rollup-linux-loong64-gnu': 4.56.0 - '@rollup/rollup-linux-loong64-musl': 4.56.0 - '@rollup/rollup-linux-ppc64-gnu': 4.56.0 - '@rollup/rollup-linux-ppc64-musl': 4.56.0 - '@rollup/rollup-linux-riscv64-gnu': 4.56.0 - '@rollup/rollup-linux-riscv64-musl': 4.56.0 - '@rollup/rollup-linux-s390x-gnu': 4.56.0 - '@rollup/rollup-linux-x64-gnu': 4.56.0 - '@rollup/rollup-linux-x64-musl': 4.56.0 - '@rollup/rollup-openbsd-x64': 4.56.0 - '@rollup/rollup-openharmony-arm64': 4.56.0 - '@rollup/rollup-win32-arm64-msvc': 4.56.0 - '@rollup/rollup-win32-ia32-msvc': 4.56.0 - '@rollup/rollup-win32-x64-gnu': 4.56.0 - '@rollup/rollup-win32-x64-msvc': 4.56.0 + '@rollup/rollup-android-arm-eabi': 4.57.1 + '@rollup/rollup-android-arm64': 4.57.1 + '@rollup/rollup-darwin-arm64': 4.57.1 + '@rollup/rollup-darwin-x64': 4.57.1 + '@rollup/rollup-freebsd-arm64': 4.57.1 + '@rollup/rollup-freebsd-x64': 4.57.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.57.1 + '@rollup/rollup-linux-arm-musleabihf': 4.57.1 + '@rollup/rollup-linux-arm64-gnu': 4.57.1 + '@rollup/rollup-linux-arm64-musl': 4.57.1 + '@rollup/rollup-linux-loong64-gnu': 4.57.1 + '@rollup/rollup-linux-loong64-musl': 4.57.1 + '@rollup/rollup-linux-ppc64-gnu': 4.57.1 + '@rollup/rollup-linux-ppc64-musl': 4.57.1 + '@rollup/rollup-linux-riscv64-gnu': 4.57.1 + '@rollup/rollup-linux-riscv64-musl': 4.57.1 + '@rollup/rollup-linux-s390x-gnu': 4.57.1 + '@rollup/rollup-linux-x64-gnu': 4.57.1 + '@rollup/rollup-linux-x64-musl': 4.57.1 + '@rollup/rollup-openbsd-x64': 4.57.1 + '@rollup/rollup-openharmony-arm64': 4.57.1 + '@rollup/rollup-win32-arm64-msvc': 4.57.1 + '@rollup/rollup-win32-ia32-msvc': 4.57.1 + '@rollup/rollup-win32-x64-gnu': 4.57.1 + '@rollup/rollup-win32-x64-msvc': 4.57.1 fsevents: 2.3.3 rpc-websockets@9.3.3: @@ -8536,16 +9797,10 @@ snapshots: semver@7.7.3: {} - seroval-plugins@1.3.3(seroval@1.3.2): - dependencies: - seroval: 1.3.2 - seroval-plugins@1.5.0(seroval@1.5.0): dependencies: seroval: 1.5.0 - seroval@1.3.2: {} - seroval@1.5.0: {} set-blocking@2.0.0: {} @@ -8643,20 +9898,20 @@ snapshots: slow-redact@0.3.2: {} - solid-js@1.9.10: + solid-js@1.9.11: dependencies: csstype: 3.2.3 - seroval: 1.3.2 - seroval-plugins: 1.3.3(seroval@1.3.2) + seroval: 1.5.0 + seroval-plugins: 1.5.0(seroval@1.5.0) sonic-boom@4.2.0: dependencies: atomic-sleep: 1.0.0 - sonner@2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + sonner@2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) source-map-js@1.2.1: {} @@ -8749,11 +10004,11 @@ snapshots: tinyrainbow@3.0.3: {} - tldts-core@7.0.19: {} + tldts-core@7.0.22: {} - tldts@7.0.19: + tldts@7.0.22: dependencies: - tldts-core: 7.0.19 + tldts-core: 7.0.22 to-buffer@1.2.2: dependencies: @@ -8771,7 +10026,7 @@ snapshots: tough-cookie@6.0.0: dependencies: - tldts: 7.0.19 + tldts: 7.0.22 tr46@0.0.3: {} @@ -8792,6 +10047,33 @@ snapshots: tty-browserify@0.0.1: {} + turbo-darwin-64@2.8.3: + optional: true + + turbo-darwin-arm64@2.8.3: + optional: true + + turbo-linux-64@2.8.3: + optional: true + + turbo-linux-arm64@2.8.3: + optional: true + + turbo-windows-64@2.8.3: + optional: true + + turbo-windows-arm64@2.8.3: + optional: true + + turbo@2.8.3: + optionalDependencies: + turbo-darwin-64: 2.8.3 + turbo-darwin-arm64: 2.8.3 + turbo-linux-64: 2.8.3 + turbo-linux-arm64: 2.8.3 + turbo-windows-64: 2.8.3 + turbo-windows-arm64: 2.8.3 + tw-animate-css@1.4.0: {} tweetnacl@1.0.3: {} @@ -8829,10 +10111,12 @@ snapshots: undici-types@7.16.0: {} - undici-types@7.19.1: + undici-types@7.19.2: optional: true - undici@7.19.1: {} + undici@7.19.2: {} + + undici@7.20.0: {} unplugin@2.3.11: dependencies: @@ -8847,7 +10131,7 @@ snapshots: chokidar: 5.0.0 destr: 2.0.5 h3: 1.15.5 - lru-cache: 11.2.4 + lru-cache: 11.2.5 node-fetch-native: 1.6.7 ofetch: 1.5.1 ufo: 1.6.3 @@ -8865,13 +10149,13 @@ snapshots: punycode: 1.4.1 qs: 6.14.1 - use-sync-external-store@1.4.0(react@19.2.3): + use-sync-external-store@1.4.0(react@19.2.4): dependencies: - react: 19.2.3 + react: 19.2.4 - use-sync-external-store@1.6.0(react@19.2.3): + use-sync-external-store@1.6.0(react@19.2.4): dependencies: - react: 19.2.3 + react: 19.2.4 utf-8-validate@5.0.10: dependencies: @@ -8895,14 +10179,14 @@ snapshots: uuid@9.0.1: {} - valtio@2.1.7(@types/react@19.2.9)(react@19.2.3): + valtio@2.1.7(@types/react@19.2.11)(react@19.2.4): dependencies: proxy-compare: 3.0.1 optionalDependencies: - '@types/react': 19.2.9 - react: 19.2.3 + '@types/react': 19.2.11 + react: 19.2.4 - viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4): + viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -8919,7 +10203,7 @@ snapshots: - utf-8-validate - zod - viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -8936,43 +10220,43 @@ snapshots: - utf-8-validate - zod - vite-plugin-node-polyfills@0.25.0(rollup@4.56.0)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): + vite-plugin-node-polyfills@0.25.0(rollup@4.57.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - '@rollup/plugin-inject': 5.0.5(rollup@4.56.0) + '@rollup/plugin-inject': 5.0.5(rollup@4.57.1) node-stdlib-browser: 1.3.1 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - rollup - vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.56.0 + rollup: 4.57.1 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.0.10 + '@types/node': 25.2.0 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.2 tsx: 4.21.0 yaml: 2.8.2 - vitest-browser-react@2.0.4(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vitest@4.0.18): + vitest-browser-react@2.0.5(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18): dependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - vitest: 4.0.18(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + vitest: 4.0.18(@types/node@25.2.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: - '@types/react': 19.2.9 - '@types/react-dom': 19.2.3(@types/react@19.2.9) + '@types/react': 19.2.11 + '@types/react-dom': 19.2.3(@types/react@19.2.11) - vitest@4.0.18(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.0.18(@types/node@25.2.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -8989,12 +10273,12 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.0.10 - '@vitest/browser-playwright': 4.0.18(bufferutil@4.1.0)(playwright@1.58.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) - jsdom: 27.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@types/node': 25.2.0 + '@vitest/browser-playwright': 4.0.18(bufferutil@4.1.0)(playwright@1.58.0)(utf-8-validate@5.0.10)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18) + jsdom: 28.0.0(@noble/hashes@1.8.0) transitivePeerDependencies: - jiti - less @@ -9014,14 +10298,14 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - wagmi@3.4.1(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.3))(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)): + wagmi@3.4.2(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.4))(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)): dependencies: - '@tanstack/react-query': 5.90.20(react@19.2.3) - '@wagmi/connectors': 7.1.5(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(typescript@5.9.3)(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@wagmi/core': 3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) - react: 19.2.3 - use-sync-external-store: 1.4.0(react@19.2.3) - viem: 2.45.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@tanstack/react-query': 5.90.20(react@19.2.4) + '@wagmi/connectors': 7.1.6(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(typescript@5.9.3)(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 3.3.2(@tanstack/query-core@5.90.20)(@types/react@19.2.11)(ox@0.11.3(typescript@5.9.3)(zod@3.25.76))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + react: 19.2.4 + use-sync-external-store: 1.4.0(react@19.2.4) + viem: 2.45.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -9044,12 +10328,15 @@ snapshots: webpack-virtual-modules@0.6.2: {} - whatwg-mimetype@4.0.0: {} + whatwg-mimetype@5.0.0: {} - whatwg-url@15.1.0: + whatwg-url@16.0.0(@noble/hashes@1.8.0): dependencies: + '@exodus/bytes': 1.11.0(@noble/hashes@1.8.0) tr46: 6.0.0 webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' whatwg-url@5.0.0: dependencies: @@ -9153,15 +10440,28 @@ snapshots: zod@3.25.76: {} - zustand@5.0.0(@types/react@19.2.9)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)): + zustand@5.0.0(@types/react@19.2.11)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)): + optionalDependencies: + '@types/react': 19.2.11 + react: 19.2.4 + use-sync-external-store: 1.4.0(react@19.2.4) + + zustand@5.0.0(@types/react@19.2.11)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): + optionalDependencies: + '@types/react': 19.2.11 + react: 19.2.4 + use-sync-external-store: 1.6.0(react@19.2.4) + + zustand@5.0.3(@types/react@19.2.11)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)): optionalDependencies: - '@types/react': 19.2.9 - react: 19.2.3 - use-sync-external-store: 1.4.0(react@19.2.3) + '@types/react': 19.2.11 + react: 19.2.4 + use-sync-external-store: 1.4.0(react@19.2.4) + optional: true - zustand@5.0.3(@types/react@19.2.9)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)): + zustand@5.0.3(@types/react@19.2.11)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): optionalDependencies: - '@types/react': 19.2.9 - react: 19.2.3 - use-sync-external-store: 1.4.0(react@19.2.3) + '@types/react': 19.2.11 + react: 19.2.4 + use-sync-external-store: 1.6.0(react@19.2.4) optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 08ba5d2..64562b7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,65 @@ packages: - - "!opensrc/**" + - packages/* + - apps/* + +catalog: + '@base-ui/react': ^1.1.0 + '@biomejs/biome': ^2.3.14 + '@commitlint/cli': ^20.4.1 + '@commitlint/config-conventional': ^20.4.1 + '@effect-atom/atom-react': ^0.5.0 + '@effect/experimental': ^0.58.0 + '@effect/language-service': ^0.73.0 + '@effect/platform': ^0.94.3 + '@effect/platform-node': ^0.104.1 + '@ledgerhq/wallet-api-client': ^1.12.6 + '@lucas-barake/effect-form-react': ^0.18.0 + '@reown/appkit': ^1.8.17 + '@reown/appkit-adapter-wagmi': ^1.8.17 + '@stakekit/common': ^0.0.61 + '@tailwindcss/vite': ^4.0.6 + '@tanstack/devtools-vite': ^0.5.0 + '@tanstack/react-devtools': ^0.9.4 + '@tanstack/react-query': ^5.90.20 + '@tanstack/react-router': ^1.158.0 + '@tanstack/react-router-devtools': ^1.158.0 + '@tanstack/react-virtual': ^3.13.18 + '@tanstack/router-cli': ^1.158.0 + '@tanstack/router-plugin': ^1.157.8 + '@testing-library/dom': ^10.4.0 + '@testing-library/react': ^16.3.2 + '@tim-smart/openapi-gen': ^0.4.13 + '@types/node': ^25.2.0 + '@types/react': ^19.2.11 + '@types/react-dom': ^19.2.0 + '@vite-pwa/assets-generator': ^1.0.2 + '@vitejs/plugin-react': ^5.1.3 + '@vitest/browser-playwright': ^4.0.18 + babel-plugin-react-compiler: ^1.0.0 + class-variance-authority: ^0.7.1 + clsx: ^2.1.1 + effect: ^3.19.16 + husky: ^9.1.7 + jsdom: ^28.0.0 + lucide-react: ^0.563.0 + openapi-filter: ^3.2.3 + react: ^19.2.0 + react-dom: ^19.2.0 + sonner: ^2.0.7 + tailwind-merge: ^3.0.2 + tailwindcss: ^4.0.6 + tsx: ^4.21.0 + turbo: ^2.8.3 + tw-animate-css: ^1.3.6 + typescript: ^5.7.2 + viem: ^2.45.0 + vite: ^7.3.1 + vite-plugin-node-polyfills: ^0.25.0 + vitest: ^4.0.18 + vitest-browser-react: ^2.0.4 + wagmi: ^3.4.2 + +nodeLinker: hoisted patchedDependencies: '@tim-smart/openapi-gen': patches/@tim-smart__openapi-gen.patch diff --git a/src/atoms/config-atom.ts b/src/atoms/config-atom.ts deleted file mode 100644 index 175585f..0000000 --- a/src/atoms/config-atom.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Effect } from "effect"; -import { ConfigService } from "@/services/config"; -import { runtimeAtom } from "@/services/runtime"; - -export const configAtom = runtimeAtom.atom( - Effect.gen(function* () { - const config = yield* ConfigService; - - return config; - }), -); diff --git a/src/components/modules/Order/Overview/utils.ts b/src/components/modules/Order/Overview/utils.ts deleted file mode 100644 index 40534f7..0000000 --- a/src/components/modules/Order/Overview/utils.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Option } from "effect"; -import type { TPOrSLSettings } from "@/components/molecules/tp-sl-dialog"; - -export function formatTPOrSLSettings( - settings: TPOrSLSettings, - side: "long" | "short" = "long", -): string { - const tp = Option.fromNullable(settings.takeProfit.percentage).pipe( - Option.filter((percentage) => percentage !== 0), - Option.map((percentage) => - side === "short" ? `TP -${percentage}%` : `TP +${percentage}%`, - ), - Option.getOrElse(() => "TP Off"), - ); - - const sl = Option.fromNullable(settings.stopLoss.percentage).pipe( - Option.filter((percentage) => percentage !== 0), - Option.map((percentage) => - side === "short" ? `SL +${percentage}%` : `SL -${percentage}%`, - ), - Option.getOrElse(() => "SL Off"), - ); - - return `${tp}, ${sl}`; -} diff --git a/src/components/modules/PositionDetails/Overview/overview-tab-content.tsx b/src/components/modules/PositionDetails/Overview/overview-tab-content.tsx deleted file mode 100644 index 0b9dcf6..0000000 --- a/src/components/modules/PositionDetails/Overview/overview-tab-content.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { Card, CardSection } from "@/components/ui/card"; -import { formatAmount, formatRate } from "@/lib/utils"; -import type { MarketDto } from "@/services/api-client/client-factory"; - -function formatVolume(volume: number): string { - if (volume >= 1_000_000_000) { - return `$${(volume / 1_000_000_000).toFixed(2)}B`; - } - if (volume >= 1_000_000) { - return `$${(volume / 1_000_000).toFixed(2)}M`; - } - if (volume >= 1_000) { - return `$${(volume / 1_000).toFixed(2)}K`; - } - return formatAmount(volume); -} - -export function OverviewTabContent({ market }: { market: MarketDto }) { - const currentPrice = market.markPrice; - const priceChange24h = market.priceChange24h; - - const estimatedLow = currentPrice - Math.abs(priceChange24h); - const estimatedHigh = currentPrice + Math.abs(priceChange24h); - - return ( - - -
- - 24h low - - - {formatAmount(estimatedLow)} - -
-
- - 24h high - - - {formatAmount(estimatedHigh)} - -
-
- -
- - 24h volume - - - {formatVolume(market.volume24h)} - -
-
- - Open Interest - - - {formatVolume(market.openInterest)} - -
-
- -
- - Funding Rate - - - {formatRate(market.fundingRate, { maximumFractionDigits: 4 })} - -
-
- - Funding Interval - - - {market.fundingRateIntervalHours}h - -
-
- -
- - Maker Fee - - - {market.makerFee ? formatRate(market.makerFee) : "-"} - -
-
- - Taker Fee - - - {market.takerFee ? formatRate(market.takerFee) : "-"} - -
-
-
- ); -} diff --git a/src/components/ui/divider.tsx b/src/components/ui/divider.tsx deleted file mode 100644 index c68df7b..0000000 --- a/src/components/ui/divider.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export const Divider = () => { - return
; -}; diff --git a/src/domain/chains/index.ts b/src/domain/chains/index.ts deleted file mode 100644 index 441c8b4..0000000 --- a/src/domain/chains/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { SupportedEvmChain } from "@/domain/chains/evm"; - -export type SupportedSKChains = SupportedEvmChain; diff --git a/src/domain/market.ts b/src/domain/market.ts deleted file mode 100644 index a7941be..0000000 --- a/src/domain/market.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Array as _Array, Option } from "effect"; -import type { MarketDto } from "@/services/api-client/api-schemas"; - -export const MIN_LEVERAGE = 1; -export const MAX_LEVERAGE = 40; - -export const getMaxLeverage = (leverages: MarketDto["leverageRange"]): number => - _Array.last(leverages).pipe(Option.getOrElse(() => MAX_LEVERAGE)); diff --git a/src/domain/position.ts b/src/domain/position.ts deleted file mode 100644 index f6b1b0f..0000000 --- a/src/domain/position.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { PositionDto } from "@/services/api-client/api-schemas"; - -export const getPriceChangePercent = ({ - currentPrice, - pastOrFuturePrice, -}: { - currentPrice: number; - pastOrFuturePrice: number; -}) => { - if (pastOrFuturePrice === 0) return 100; - return ((currentPrice - pastOrFuturePrice) / pastOrFuturePrice) * 100; -}; - -export const getPositionChangePercent = (position: PositionDto) => - getPriceChangePercent({ - currentPrice: position.markPrice, - pastOrFuturePrice: position.entryPrice, - }); - -export const getCloseCalculations = ( - position: PositionDto, - closePercentage: number, -) => { - const ratio = closePercentage / 100; - const sizeNum = Number.parseFloat(position.size); - const positionValue = position.markPrice * sizeNum; - const closeValue = positionValue * ratio; - const marginReturn = position.margin * ratio; - const pnlReturn = position.unrealizedPnl * ratio; - const youWillReceive = marginReturn + pnlReturn; - const closeSize = sizeNum * ratio; - const closeSizeInMarketPrice = (closeSize * position.markPrice).toFixed(6); - - return { - closeValue, - marginReturn, - pnlReturn, - youWillReceive, - closeSize, - closeSizeInMarketPrice, - }; -}; - -export const getLiquidationPrice = ({ - currentPrice, - leverage, - side, -}: { - currentPrice: number; - leverage: number; - side: "long" | "short"; -}) => - side === "long" - ? currentPrice * (1 - 1 / leverage) - : currentPrice * (1 + 1 / leverage); - -export const getPriceChangePercentToLiquidation = ({ - currentPrice, - liquidationPrice, -}: { - currentPrice: number; - liquidationPrice: number; -}) => { - return getPriceChangePercent({ - currentPrice, - pastOrFuturePrice: liquidationPrice, - }); -}; - -export const MIN_LEVERAGE = 1; -export const MAX_LEVERAGE = 40; - -export const getLeveragePercent = ({ - leverage, - maxLeverage, -}: { - leverage: number; - maxLeverage: number; -}) => ((leverage - MIN_LEVERAGE) / (maxLeverage - MIN_LEVERAGE)) * 100; - -export const calculateMargin = ({ - positionSize, - leverage, -}: { - positionSize: number; - leverage: number; -}) => { - if (leverage <= 0) return positionSize; - return positionSize / leverage; -}; - -export const calculatePositionSize = ({ - margin, - leverage, -}: { - margin: number; - leverage: number; -}) => margin * leverage; diff --git a/src/lib/utils.ts b/src/lib/utils.ts deleted file mode 100644 index f504794..0000000 --- a/src/lib/utils.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { type ClassValue, clsx } from "clsx"; -import { Match } from "effect"; -import { twMerge } from "tailwind-merge"; -import type { TokenString } from "@/domain/types"; -import type { Networks, TokenDto } from "@/services/api-client/api-schemas"; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} - -export const getNetworkLogo = (network: typeof Networks.Type) => - `https://assets.stakek.it/networks/${network}.svg`; - -export const getTokenLogo = (tokenSymbol: string) => - `https://assets.stakek.it/tokens/${tokenSymbol.toLowerCase()}.svg`; - -const tokenAmountFormatter = new Intl.NumberFormat("en-US", { - minimumFractionDigits: 2, - maximumFractionDigits: 5, -}); - -export const formatTokenAmount = ({ - amount, - symbol, -}: { - amount: number | string; - symbol: string; -}) => { - const parsedAmount = typeof amount === "string" ? parseFloat(amount) : amount; - return `${symbol} ${tokenAmountFormatter.format(parsedAmount)}`; -}; - -export const formatAmount = ( - amount: number | string, - options = { symbol: "$" } as { - minimumFractionDigits?: number; - maximumFractionDigits: number; - withSign?: boolean; - symbol?: string | null; - }, -): string => { - const amountNumber = typeof amount === "string" ? parseFloat(amount) : amount; - - const symbol = options.symbol !== null ? (options.symbol ?? "$") : ""; - - const { maxDigits, minDigits } = Match.value({ amountNumber, options }).pipe( - Match.withReturnType<{ - minDigits: number | undefined; - maxDigits: number | undefined; - }>(), - Match.when({ options: Match.defined }, (v) => ({ - minDigits: v.options.minimumFractionDigits, - maxDigits: v.options.maximumFractionDigits, - })), - Match.when( - ({ amountNumber }) => amountNumber >= 1000, - () => ({ minDigits: undefined, maxDigits: 0 }), - ), - Match.when( - ({ amountNumber }) => amountNumber >= 1, - () => ({ minDigits: 2, maxDigits: 4 }), - ), - Match.when( - ({ amountNumber }) => amountNumber >= 0.01, - () => ({ minDigits: undefined, maxDigits: 4 }), - ), - Match.when( - ({ amountNumber }) => amountNumber === 0, - () => ({ minDigits: 2, maxDigits: undefined }), - ), - Match.orElse(() => ({ minDigits: 6, maxDigits: undefined })), - ); - - return `${symbol}${amountNumber.toLocaleString("en-US", { - minimumFractionDigits: minDigits, - maximumFractionDigits: maxDigits, - signDisplay: options?.withSign ? "always" : "auto", - })}`; -}; - -export const formatPercentage = ( - percentage: number, - options?: { - maximumFractionDigits?: number; - }, -) => { - return `${percentage.toFixed(options?.maximumFractionDigits ?? 2)}%`; -}; - -export const formatRate = ( - rate: string, - options?: { - maximumFractionDigits?: number; - }, -) => { - return formatPercentage(Number.parseFloat(rate) * 100, options); -}; - -export const getTokenString = (token: TokenDto): TokenString => - `${token.network}-${token.address}`; - -export const truncateAddress = (address: string, length: number = 6) => - `${address.slice(0, length)}...${address.slice(-length)}`; - -export const formatSnakeCase = (network: string) => { - let str = network[0].toUpperCase(); - for (let i = 1; i < network.length; i++) { - str += network[i] === "_" ? ` ${network[++i].toUpperCase()}` : network[i]; - } - return str; -}; - -export const formatDate = (timestamp: number): string => { - const date = new Date(timestamp); - return date.toLocaleDateString("en-US", { - day: "numeric", - month: "short", - hour: "numeric", - minute: "2-digit", - hour12: true, - }); -}; - -export const truncateNumber = (number: number, precision: number = 2) => { - return Math.floor(number * 10 ** precision) / 10 ** precision; -}; diff --git a/src/main.css b/src/main.css deleted file mode 100644 index e1f1dba..0000000 --- a/src/main.css +++ /dev/null @@ -1,13 +0,0 @@ -html { - background: #131517; -} - -#app { - min-height: 100vh; - padding: 32px; - display: flex; - justify-content: center; - align-items: flex-start; - padding-top: 60px; - background: #131517; -} diff --git a/src/styles.css b/src/styles.css deleted file mode 100644 index 090eda7..0000000 --- a/src/styles.css +++ /dev/null @@ -1,170 +0,0 @@ -@import "tailwindcss"; - -@import "tw-animate-css"; - -@custom-variant dark (&:is(.dark *)); - -:root { - --font-family: - -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", - "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; - --background: oklch(1 0 0); - --foreground: oklch(0.141 0.005 285.823); - --card: oklch(1 0 0); - --card-foreground: oklch(0.141 0.005 285.823); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.141 0.005 285.823); - --primary: oklch(0.21 0.006 285.885); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.967 0.001 286.375); - --secondary-foreground: oklch(0.21 0.006 285.885); - --muted: oklch(0.967 0.001 286.375); - --muted-foreground: oklch(0.552 0.016 285.938); - --accent: oklch(0.967 0.001 286.375); - --accent-foreground: oklch(0.21 0.006 285.885); - --destructive: oklch(0.577 0.245 27.325); - --destructive-foreground: oklch(0.577 0.245 27.325); - --border: oklch(0.92 0.004 286.32); - --input: oklch(0.92 0.004 286.32); - --ring: oklch(0.871 0.006 286.286); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --radius: 0.625rem; - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.141 0.005 285.823); - --sidebar-primary: oklch(0.21 0.006 285.885); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.967 0.001 286.375); - --sidebar-accent-foreground: oklch(0.21 0.006 285.885); - --sidebar-border: oklch(0.92 0.004 286.32); - --sidebar-ring: oklch(0.871 0.006 286.286); - - --gray-1: #f6f7f999; - --gray-2: #999999; - --gray-3: #ffffff0d; - --gray-4: #ffffff40; - --gray-5: #ffffff2e; - - --accent-green: #34c759; - --accent-red: #ff383c; -} - -.dark { - --background: #000000b2; - --foreground: oklch(0.985 0 0); - --card: oklch(0.141 0.005 285.823); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.141 0.005 285.823); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.985 0 0); - --primary-foreground: oklch(0.21 0.006 285.885); - --secondary: oklch(0.274 0.006 286.033); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.274 0.006 286.033); - --muted-foreground: oklch(0.705 0.015 286.067); - --accent: oklch(0.274 0.006 286.033); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.396 0.141 25.723); - --destructive-foreground: oklch(0.637 0.237 25.331); - --border: oklch(0.274 0.006 286.033); - --input: oklch(0.274 0.006 286.033); - --ring: oklch(0.442 0.017 285.786); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.21 0.006 285.885); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.274 0.006 286.033); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(0.274 0.006 286.033); - --sidebar-ring: oklch(0.442 0.017 285.786); - - --gray-1: #f6f7f999; - --gray-2: #999999; - --gray-3: #ffffff0d; - --gray-4: #ffffff40; - --gray-5: #ffffff2e; - - --accent-green: #34c759; - --accent-red: #ff383c; -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - --color-destructive: var(--destructive); - --color-destructive-foreground: var(--destructive-foreground); - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); - --color-chart-1: var(--chart-1); - --color-chart-2: var(--chart-2); - --color-chart-3: var(--chart-3); - --color-chart-4: var(--chart-4); - --color-chart-5: var(--chart-5); - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - --color-sidebar: var(--sidebar); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-ring: var(--sidebar-ring); - - --color-gray-1: var(--gray-1); - --color-gray-2: var(--gray-2); - --color-gray-3: var(--gray-3); - --color-gray-4: var(--gray-4); - --color-gray-5: var(--gray-5); - - --color-accent-green: var(--accent-green); - --color-accent-red: var(--accent-red); -} - -html { - interpolate-size: allow-keywords; -} - -body { - @apply m-0; - font-family: var(--font-family); - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: - source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; -} - -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground; - } -} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..d33d148 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,42 @@ +{ + "compilerOptions": { + "strict": true, + "composite": true, + "incremental": true, + "moduleDetection": "force", + "downlevelIteration": true, + "resolveJsonModule": true, + "esModuleInterop": false, + "skipLibCheck": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "lib": ["ES2024", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "isolatedModules": true, + "sourceMap": true, + "declarationMap": true, + "noImplicitReturns": false, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "noEmitOnError": false, + "noErrorTruncation": false, + "allowJs": false, + "checkJs": false, + "forceConsistentCasingInFileNames": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noUncheckedIndexedAccess": false, + "strictNullChecks": true, + "target": "ES2024", + "module": "esnext", + "removeComments": false, + "jsx": "react-jsx", + "plugins": [ + { + "name": "@effect/language-service" + } + ] + } +} diff --git a/tsconfig.json b/tsconfig.json index 691e07a..dab084f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,33 +1,8 @@ { - "include": ["src", "scripts", "tests"], - "compilerOptions": { - "target": "ES2022", - "jsx": "react-jsx", - "module": "ESNext", - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "types": ["vite/client"], - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": true, - - /* Linting */ - "skipLibCheck": true, - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true, - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - }, - "plugins": [ - { - "name": "@effect/language-service" - } - ] - } + "files": [], + "references": [ + { "path": "./packages/common" }, + { "path": "./packages/widget" }, + { "path": "./packages/dashboard" } + ] } diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..deab565 --- /dev/null +++ b/turbo.json @@ -0,0 +1,23 @@ +{ + "$schema": "./node_modules/turbo/schema.json", + "tasks": { + "dev": { + "persistent": true, + "cache": false + }, + "build": { + "outputs": ["dist/**"], + "dependsOn": ["^build"] + }, + "lint": { + "inputs": ["src/**", "tests/**"] + }, + "format": { + "inputs": ["src/**", "tests/**"] + }, + "test": { + "inputs": ["src/**", "tests/**"] + } + }, + "dangerouslyDisablePackageManagerCheck": true +} From fec5c655e7b8914528e3624e2543ff12b385d0e8 Mon Sep 17 00:00:00 2001 From: Petar Todorovic Date: Thu, 5 Feb 2026 14:51:23 +0100 Subject: [PATCH 2/3] refactor: misc --- .../common/src/atoms/close-position-atoms.ts | 56 +++ .../common/src/atoms/edit-position-atoms.ts | 73 +++ packages/common/src/atoms/index.ts | 3 + packages/common/src/atoms/order-form-atoms.ts | 269 +++++++++++ .../atoms/position-pending-actions-atom.ts | 18 +- packages/common/src/hooks/index.ts | 5 + .../common/src/hooks/use-close-position.ts | 33 ++ packages/common/src/hooks/use-deposit-form.ts | 305 +++++++++++++ .../common/src/hooks/use-edit-position.ts | 28 ++ packages/common/src/hooks/use-order-form.ts | 213 +++++++++ .../common/src/hooks/use-withdraw-form.ts | 180 ++++++++ .../modules/trade/order-form/index.tsx | 53 +-- .../modules/trade/order-form/state.tsx | 419 ------------------ .../header/deposit/deposit-dialog.tsx | 8 +- .../molecules/header/deposit/state.tsx | 222 ++-------- .../src/components/molecules/header/index.tsx | 10 +- .../molecules/header/withdraw/state.tsx | 124 +----- .../header/withdraw/withdraw-dialog.tsx | 2 +- .../positions/close-position-dialog.tsx | 10 +- .../molecules/positions/positions-tab.tsx | 3 +- .../components/molecules/positions/state.ts | 178 -------- .../modules/Account/Deposit/index.tsx | 19 +- .../modules/Account/Deposit/state.tsx | 278 +----------- .../modules/Account/Withdraw/index.tsx | 21 +- .../modules/Account/Withdraw/state.tsx | 178 +------- .../modules/Order/Overview/index.tsx | 43 +- .../modules/Order/Overview/loading.tsx | 2 +- .../modules/Order/Overview/state.tsx | 380 ---------------- .../modules/PositionDetails/Close/index.tsx | 14 +- .../modules/PositionDetails/Close/state.tsx | 101 +---- .../Overview/Position/index.tsx | 3 +- .../Overview/Position/state.ts | 102 ----- 32 files changed, 1341 insertions(+), 2012 deletions(-) create mode 100644 packages/common/src/atoms/close-position-atoms.ts create mode 100644 packages/common/src/atoms/edit-position-atoms.ts create mode 100644 packages/common/src/atoms/order-form-atoms.ts create mode 100644 packages/common/src/hooks/use-close-position.ts create mode 100644 packages/common/src/hooks/use-deposit-form.ts create mode 100644 packages/common/src/hooks/use-edit-position.ts create mode 100644 packages/common/src/hooks/use-order-form.ts create mode 100644 packages/common/src/hooks/use-withdraw-form.ts delete mode 100644 packages/dashboard/src/components/modules/trade/order-form/state.tsx delete mode 100644 packages/dashboard/src/components/molecules/positions/state.ts delete mode 100644 packages/widget/src/components/modules/Order/Overview/state.tsx delete mode 100644 packages/widget/src/components/modules/PositionDetails/Overview/Position/state.ts diff --git a/packages/common/src/atoms/close-position-atoms.ts b/packages/common/src/atoms/close-position-atoms.ts new file mode 100644 index 0000000..e72d916 --- /dev/null +++ b/packages/common/src/atoms/close-position-atoms.ts @@ -0,0 +1,56 @@ +import { Atom, Registry, Result } from "@effect-atom/atom-react"; +import { Number as _Number, Effect } from "effect"; +import type { WalletConnected } from "../domain/wallet"; +import { getCloseCalculations } from "../lib/math"; +import { ApiClientService } from "../services/api-client"; +import type { PositionDto } from "../services/api-client/api-schemas"; +import { runtimeAtom } from "../services/runtime"; +import { actionAtom } from "./actions-atoms"; +import { selectedProviderAtom } from "./providers-atoms"; + +export const SLIDER_STOPS = [0, 25, 50, 75, 100]; + +export const closePercentageAtom = Atom.writable( + () => 25, + (ctx, value) => + ctx.setSelf(_Number.clamp({ minimum: 0, maximum: 100 })(value)), +); + +export const submitCloseAtom = runtimeAtom.fn( + Effect.fn(function* (args: { + position: PositionDto; + wallet: WalletConnected; + }) { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + const closePercentage = registry.get(closePercentageAtom); + const closeCalculations = + closePercentage === 100 + ? null + : getCloseCalculations(args.position, closePercentage); + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: args.wallet.currentAccount.address, + action: "close", + args: { + marketId: args.position.marketId, + side: args.position.side, + ...(closeCalculations && { + size: closeCalculations.closeSizeInMarketPrice, + }), + }, + }); + + registry.set(actionAtom, action); + }), +); diff --git a/packages/common/src/atoms/edit-position-atoms.ts b/packages/common/src/atoms/edit-position-atoms.ts new file mode 100644 index 0000000..9171492 --- /dev/null +++ b/packages/common/src/atoms/edit-position-atoms.ts @@ -0,0 +1,73 @@ +import { Atom, Registry, Result } from "@effect-atom/atom-react"; +import { Effect, Option } from "effect"; +import type { + TPOrSLConfiguration, + TPOrSLOption, + TPOrSLSettings, +} from "../components/molecules/tp-sl-dialog"; +import type { WalletConnected } from "../domain/wallet"; +import { ApiClientService } from "../services/api-client"; +import type { + ArgumentsDto, + PositionDto, +} from "../services/api-client/api-schemas"; +import { runtimeAtom } from "../services/runtime"; +import { actionAtom } from "./actions-atoms"; +import { selectedProviderAtom } from "./providers-atoms"; + +export const tpSlArgument = (tpOrSL: TPOrSLConfiguration) => + Option.some(tpOrSL).pipe( + Option.filterMap((v) => + v.triggerPrice !== null && v.option !== null + ? Option.some(v.triggerPrice) + : Option.none(), + ), + Option.getOrUndefined, + ); + +export const editSLTPAtom = Atom.family((actionType: TPOrSLOption) => + runtimeAtom.fn( + Effect.fn(function* ({ + position, + wallet, + tpOrSLSettings, + }: { + position: PositionDto; + wallet: WalletConnected; + tpOrSLSettings: TPOrSLSettings; + }) { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + const newStopLossPrice: ArgumentsDto["stopLossPrice"] = tpSlArgument( + tpOrSLSettings.stopLoss, + ); + + const newTakeProfitPrice: ArgumentsDto["takeProfitPrice"] = tpSlArgument( + tpOrSLSettings.takeProfit, + ); + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: wallet.currentAccount.address, + action: actionType, + args: { + marketId: position.marketId, + ...(actionType === "stopLoss" + ? { stopLossPrice: newStopLossPrice } + : { takeProfitPrice: newTakeProfitPrice }), + }, + }); + + registry.set(actionAtom, action); + }), + ), +); diff --git a/packages/common/src/atoms/index.ts b/packages/common/src/atoms/index.ts index 4d5d933..568b037 100644 --- a/packages/common/src/atoms/index.ts +++ b/packages/common/src/atoms/index.ts @@ -1,6 +1,9 @@ export * from "./actions-atoms"; +export * from "./close-position-atoms"; export * from "./config-atom"; +export * from "./edit-position-atoms"; export * from "./markets-atoms"; +export * from "./order-form-atoms"; export * from "./orders-pending-actions-atom"; export * from "./portfolio-atoms"; export * from "./position-pending-actions-atom"; diff --git a/packages/common/src/atoms/order-form-atoms.ts b/packages/common/src/atoms/order-form-atoms.ts new file mode 100644 index 0000000..31c842e --- /dev/null +++ b/packages/common/src/atoms/order-form-atoms.ts @@ -0,0 +1,269 @@ +import { Atom, Registry, Result } from "@effect-atom/atom-react"; +import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; +import { Number as _Number, Effect, Schema } from "effect"; +import { AmountField, type TPOrSLSettings } from "../components"; +import { isWalletConnected, type WalletConnected } from "../domain"; +import { + calcBaseAmountFromUsd, + calculateMargin, + calculatePositionSize, + clampPercent, + getLiquidationPrice, + getMaxLeverage, + MIN_LEVERAGE, + percentOf, + round, + valueFromPercent, +} from "../lib"; +import { + ApiClientService, + ApiSchemas, + type ApiTypes, + runtimeAtom, +} from "../services"; +import { actionAtom } from "./actions-atoms"; +import { tpSlArgument } from "./edit-position-atoms"; +import { selectedProviderBalancesAtom } from "./portfolio-atoms"; +import { selectedProviderAtom } from "./providers-atoms"; +import { walletAtom } from "./wallet-atom"; + +// Constants +export const ORDER_SLIDER_STOPS = [0, 25, 50, 75, 100]; + +// Types +export type OrderType = "market" | "limit"; +export type OrderSide = ApiTypes.PositionDtoSide; + +// Schemas +export const LeverageRangesSchema = Schema.Data( + ApiSchemas.MarketDto.fields.leverageRange, +).pipe(Schema.brand("LeverageRange")); + +// Order Type Atom +export const orderTypeAtom = Atom.writable( + () => "market", + (ctx, value) => ctx.setSelf(value), +); + +// Order Side Atom +export const orderSideAtom = Atom.writable( + () => "long", + (ctx, value) => ctx.setSelf(value), +); + +// Leverage Atom (family keyed by leverage ranges) +export const leverageAtom = Atom.family( + (leverageRanges: typeof LeverageRangesSchema.Type) => + Atom.writable( + () => getMaxLeverage(leverageRanges), + (ctx, value: number) => + ctx.setSelf( + _Number.clamp({ + minimum: MIN_LEVERAGE, + maximum: getMaxLeverage(leverageRanges), + })(value), + ), + ), +); + +// TP/SL Settings Atom +export const tpOrSLSettingsAtom = Atom.writable( + () => ({ + takeProfit: { + option: null, + triggerPrice: null, + percentage: null, + }, + stopLoss: { + option: null, + triggerPrice: null, + percentage: null, + }, + }), + (ctx, value) => ctx.setSelf(value), +); + +// Limit Price Atom +export const limitPriceAtom = Atom.writable( + () => null, + (ctx, value) => ctx.setSelf(value), +); + +// Order Form Atom (family keyed by leverage ranges) +export const orderFormAtom = Atom.family( + (leverageRanges: typeof LeverageRangesSchema.Type) => { + const orderFormBuilder = FormBuilder.empty + .addField( + "Amount", + Schema.NumberFromString.pipe( + Schema.annotations({ message: () => "Invalid amount" }), + Schema.greaterThan(0, { message: () => "Must be greater than 0" }), + ), + ) + .refineEffect((values) => + Effect.gen(function* () { + const registry = yield* Registry.AtomRegistry; + const wallet = registry + .get(walletAtom) + .pipe(Result.getOrElse(() => null)); + + if (!isWalletConnected(wallet)) { + return yield* Effect.dieMessage("No wallet"); + } + + const providerBalance = registry + .get(selectedProviderBalancesAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!providerBalance) { + return { path: ["Amount"], message: "Missing provider balance" }; + } + + const leverage = registry.get(leverageAtom(leverageRanges)); + const requiredMargin = calculateMargin({ + positionSize: values.Amount, + leverage, + }); + + if (requiredMargin > providerBalance.availableBalance) { + return { + path: ["Amount"], + message: "Insufficient balance", + }; + } + }), + ); + + const OrderForm = FormReact.make(orderFormBuilder, { + runtime: runtimeAtom, + fields: { Amount: AmountField }, + onSubmit: ( + { + wallet, + market, + side, + }: { + wallet: WalletConnected; + market: ApiSchemas.MarketDto; + side: ApiTypes.PositionDtoSide; + }, + { decoded }, + ) => + Effect.gen(function* () { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + const leverage = registry.get(leverageAtom(leverageRanges)); + const orderType = registry.get(orderTypeAtom); + const tpOrSLSettings = registry.get(tpOrSLSettingsAtom); + + const stopLossPrice = tpSlArgument(tpOrSLSettings.stopLoss); + const takeProfitPrice = tpSlArgument(tpOrSLSettings.takeProfit); + + const limitPrice = + orderType === "limit" ? registry.get(limitPriceAtom) : undefined; + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: wallet.currentAccount.address, + action: "open", + args: { + marketId: market.id, + side, + size: decoded.Amount.toString(), + marginMode: "isolated", + ...(stopLossPrice && { stopLossPrice }), + ...(takeProfitPrice && { takeProfitPrice }), + ...(leverage && { leverage }), + ...(limitPrice && { limitPrice: limitPrice }), + }, + }); + + registry.set(actionAtom, action); + }), + }); + + const setAmountFieldAtom = OrderForm.setValue(OrderForm.fields.Amount); + const amountFieldAtom = OrderForm.getFieldValue(OrderForm.fields.Amount); + + return Atom.readable(() => ({ + form: OrderForm, + setAmountFieldAtom, + amountFieldAtom, + })); + }, +); + +// Helper functions for order calculations +export const getOrderCalculations = ( + amount: number, + leverage: number, + market: ApiSchemas.MarketDto, + side: ApiTypes.PositionDtoSide, +) => { + const cryptoAmount = calcBaseAmountFromUsd({ + usdAmount: amount, + priceUsd: market.markPrice, + }); + + const liquidationPrice = getLiquidationPrice({ + currentPrice: market.markPrice, + leverage, + side, + }); + + const margin = calculateMargin({ positionSize: amount, leverage }); + + const feeRate = market.takerFee ? Number.parseFloat(market.takerFee) : 0; + const fees = amount * feeRate; + + return { + margin, + amount, + cryptoAmount, + liquidationPrice, + fees, + leverage, + }; +}; + +// Helper function for percentage change +export const calculateOrderPositionSize = ( + providerBalance: { availableBalance: number }, + leverage: number, + percentage: number, +) => { + const clampedValue = clampPercent(percentage); + const marginToUse = valueFromPercent({ + total: providerBalance.availableBalance, + percent: clampedValue, + }); + const positionSize = calculatePositionSize({ + margin: marginToUse, + leverage, + }); + + return round(positionSize); +}; + +// Helper function for calculating percentage from amount +export const calculateOrderPercentage = ( + amount: number, + leverage: number, + availableBalance: number, +) => { + const margin = calculateMargin({ positionSize: amount, leverage }); + const percentage = clampPercent( + percentOf({ part: margin, whole: availableBalance }), + ); + + return Math.round(percentage); +}; diff --git a/packages/common/src/atoms/position-pending-actions-atom.ts b/packages/common/src/atoms/position-pending-actions-atom.ts index 93566e6..786bbe8 100644 --- a/packages/common/src/atoms/position-pending-actions-atom.ts +++ b/packages/common/src/atoms/position-pending-actions-atom.ts @@ -6,12 +6,10 @@ import type { } from "../components/molecules/tp-sl-dialog"; import type { WalletAccount, WalletConnected } from "../domain/wallet"; import { ApiClientService } from "../services/api-client"; -import type { - ArgumentsDto, - PositionDto, -} from "../services/api-client/api-schemas"; +import type { PositionDto } from "../services/api-client/api-schemas"; import { runtimeAtom } from "../services/runtime"; import { actionAtom } from "./actions-atoms"; +import { tpSlArgument } from "./edit-position-atoms"; import { selectedProviderAtom } from "./providers-atoms"; export const editSLOrTPAtom = runtimeAtom.fn( @@ -37,17 +35,9 @@ export const editSLOrTPAtom = runtimeAtom.fn( return yield* Effect.dieMessage("No selected provider"); } - const newStopLossPrice: ArgumentsDto["stopLossPrice"] = - tpOrSLSettings.stopLoss.triggerPrice && - tpOrSLSettings.stopLoss.option !== null - ? tpOrSLSettings.stopLoss.triggerPrice - : undefined; + const newStopLossPrice = tpSlArgument(tpOrSLSettings.stopLoss); - const newTakeProfitPrice: ArgumentsDto["takeProfitPrice"] = - tpOrSLSettings.takeProfit.triggerPrice && - tpOrSLSettings.takeProfit.option !== null - ? tpOrSLSettings.takeProfit.triggerPrice - : undefined; + const newTakeProfitPrice = tpSlArgument(tpOrSLSettings.takeProfit); const action = yield* client.ActionsControllerExecuteAction({ providerId: selectedProvider.id, diff --git a/packages/common/src/hooks/index.ts b/packages/common/src/hooks/index.ts index 6661bfa..b6c08b6 100644 --- a/packages/common/src/hooks/index.ts +++ b/packages/common/src/hooks/index.ts @@ -1,3 +1,8 @@ +export * from "./use-close-position"; +export * from "./use-deposit-form"; +export * from "./use-edit-position"; export * from "./use-order-actions"; +export * from "./use-order-form"; export * from "./use-position-actions"; export * from "./use-tp-sl-orders"; +export * from "./use-withdraw-form"; diff --git a/packages/common/src/hooks/use-close-position.ts b/packages/common/src/hooks/use-close-position.ts new file mode 100644 index 0000000..d3c24ce --- /dev/null +++ b/packages/common/src/hooks/use-close-position.ts @@ -0,0 +1,33 @@ +import { useAtomSet, useAtomValue } from "@effect-atom/atom-react"; +import { + closePercentageAtom, + submitCloseAtom, +} from "../atoms/close-position-atoms"; +import { getCloseCalculations } from "../lib/math"; +import type { PositionDto } from "../services/api-client/api-schemas"; + +export const useClosePercentage = () => { + const closePercentage = useAtomValue(closePercentageAtom); + const setClosePercentage = useAtomSet(closePercentageAtom); + + return { + closePercentage, + setClosePercentage, + }; +}; + +export const useCloseCalculations = (position: PositionDto) => { + const { closePercentage } = useClosePercentage(); + + return getCloseCalculations(position, closePercentage); +}; + +export const useSubmitClose = () => { + const submitResult = useAtomValue(submitCloseAtom); + const submitClose = useAtomSet(submitCloseAtom); + + return { + submitResult, + submitClose, + }; +}; diff --git a/packages/common/src/hooks/use-deposit-form.ts b/packages/common/src/hooks/use-deposit-form.ts new file mode 100644 index 0000000..f225435 --- /dev/null +++ b/packages/common/src/hooks/use-deposit-form.ts @@ -0,0 +1,305 @@ +import { + Atom, + Registry, + Result, + useAtomSet, + useAtomValue, +} from "@effect-atom/atom-react"; +import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; +import { + Array as _Array, + Effect, + Option, + Predicate, + Record, + Schema, +} from "effect"; +import { + actionAtom, + moralisTokenBalancesAtom, + selectedProviderAtom, + type TokenBalances, + walletAtom, +} from "../atoms"; +import { AmountField } from "../components"; +import { + isArbUsdcToken, + isEthNativeToken, + isWalletConnected, + makeToken, + type TokenBalance, + type WalletAccount, +} from "../domain"; +import { + calcBaseAmountFromUsd, + clampPercent, + formatTokenAmount, + percentOf, + round, + valueFromPercent, +} from "../lib"; +import { ApiClientService, runtimeAtom } from "../services"; + +export const selectedTokenBalanceAtom = Atom.family( + (walletAddress: WalletAccount["address"]) => + Atom.writable( + (ctx) => + ctx.get(moralisTokenBalancesAtom(walletAddress)).pipe( + Result.map((res) => _Array.head(res.ethereum)), + Result.map(Option.getOrNull), + ), + (ctx, value: TokenBalance) => ctx.setSelf(Result.success(value)), + ), +); + +export const depositFormBuilder = FormBuilder.empty + .addField( + "Amount", + Schema.NumberFromString.pipe( + Schema.annotations({ message: () => "Invalid amount" }), + Schema.greaterThan(0, { message: () => "Must be greater than 0" }), + ), + ) + .refineEffect((values) => + Effect.gen(function* () { + const registry = yield* Registry.AtomRegistry; + const wallet = registry + .get(walletAtom) + .pipe(Result.getOrElse(() => null)); + + if (!isWalletConnected(wallet)) { + yield* Effect.logWarning("No wallet found"); + return; + } + + const tokenBalance = registry + .get(selectedTokenBalanceAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!tokenBalance) { + return { path: ["Amount"], message: "Missing token balance" }; + } + + const cryptoAmount = calcBaseAmountFromUsd({ + usdAmount: values.Amount, + priceUsd: tokenBalance.price, + }); + + const usdMin = isArbUsdcToken(makeToken(tokenBalance.token)) ? 5 : 10; + + if (values.Amount < usdMin) { + return { path: ["Amount"], message: `Minimum deposit is $${usdMin}` }; + } + + if (Number(tokenBalance.amount) < cryptoAmount) { + return { path: ["Amount"], message: "Insufficient balance" }; + } + }), + ); + +export type DepositFormBuilder = typeof depositFormBuilder; + +const depositOnSubmit = Effect.gen(function* () { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const wallet = registry.get(walletAtom).pipe(Result.getOrElse(() => null)); + + if (!isWalletConnected(wallet)) { + return yield* Effect.dieMessage("No wallet"); + } + + const selectedTokenBalance = registry + .get(selectedTokenBalanceAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!selectedTokenBalance) { + return yield* Effect.dieMessage("No selected token balance"); + } + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + return { client, wallet, selectedTokenBalance, selectedProvider }; +}); + +export const createDepositForm = ( + amountFieldComponent: FormReact.FieldComponent, +) => + FormReact.make(depositFormBuilder, { + runtime: runtimeAtom, + fields: { Amount: amountFieldComponent }, + onSubmit: (_, { decoded }) => + Effect.gen(function* () { + const { client, wallet, selectedTokenBalance, selectedProvider } = + yield* depositOnSubmit; + + const cryptoAmount = calcBaseAmountFromUsd({ + usdAmount: decoded.Amount, + priceUsd: selectedTokenBalance.price, + }); + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: wallet.currentAccount.address, + action: "fund", + args: { + amount: cryptoAmount.toString(), + fromToken: { + network: selectedTokenBalance.token.network, + ...(!isEthNativeToken(makeToken(selectedTokenBalance.token)) && { + address: selectedTokenBalance.token.address, + }), + }, + }, + }); + + const registry = yield* Registry.AtomRegistry; + registry.set(actionAtom, action); + }), + }); + +export const DepositForm = createDepositForm(AmountField); + +const amountAtom = DepositForm.getFieldValue(DepositForm.fields.Amount); +const setAmountFieldAtom = DepositForm.setValue(DepositForm.fields.Amount); + +export const useDepositForm = () => { + const submit = useAtomSet(DepositForm.submit); + const submitResult = useAtomValue(DepositForm.submit); + + return { + submit, + submitResult, + }; +}; + +export const useSetDepositAmount = () => { + const setAmount = useAtomSet(setAmountFieldAtom); + + return { + setAmount, + }; +}; + +export const useTokenBalances = (walletAddress: WalletAccount["address"]) => { + const tokenBalances = useAtomValue( + moralisTokenBalancesAtom(walletAddress), + ).pipe(Result.getOrElse(() => Record.empty() as TokenBalances)); + + return { + tokenBalances, + }; +}; + +export const useSelectedTokenBalance = ( + walletAddress: WalletAccount["address"], +) => { + const selectedTokenBalance = useAtomValue( + selectedTokenBalanceAtom(walletAddress), + ).pipe(Result.getOrElse(() => null)); + const setSelectedTokenBalance = useAtomSet( + selectedTokenBalanceAtom(walletAddress), + ); + const setAmount = useAtomSet(setAmountFieldAtom); + + const handleSelectTokenBalance = (tokenBalance: TokenBalance) => { + setSelectedTokenBalance(tokenBalance); + setAmount("0"); + }; + + return { + selectedTokenBalance, + handleSelectTokenBalance, + }; +}; + +export const useDepositPercentage = ( + walletAddress: WalletAccount["address"], +) => { + const amount = useAtomValue(amountAtom).pipe( + Option.map(Number), + Option.filter((v) => !Number.isNaN(v)), + Option.getOrElse(() => 0), + ); + + const setAmount = useAtomSet(setAmountFieldAtom); + + const availableBalanceUsd = useAtomValue( + selectedTokenBalanceAtom(walletAddress), + ).pipe( + Result.value, + Option.filter(Predicate.isNotNull), + Option.map((balance) => Number(balance.amount) * balance.price), + Option.getOrElse(() => 0), + ); + + const percentage = clampPercent( + percentOf({ part: amount, whole: availableBalanceUsd }), + ); + + const handlePercentageChange = (newPercentage: number) => { + if (newPercentage >= 100) { + return setAmount(availableBalanceUsd.toString()); + } + + const newAmount = valueFromPercent({ + total: availableBalanceUsd, + percent: newPercentage, + }); + setAmount(round(newAmount).toString()); + }; + + return { + handlePercentageChange, + percentage: Math.round(percentage), + }; +}; + +const tokenAmountValueAtom = Atom.family( + (walletAddress: WalletAccount["address"]) => + runtimeAtom.atom((ctx) => + Effect.gen(function* () { + const tokenBalance = yield* ctx.result( + selectedTokenBalanceAtom(walletAddress), + ); + + ctx.subscribe(amountAtom, () => {}); + + const amount = ctx.get(amountAtom).pipe( + Option.map(parseFloat), + Option.filter((v) => !Number.isNaN(v)), + Option.getOrElse(() => 0), + ); + + if (!tokenBalance) { + return ""; + } + + return formatTokenAmount({ + amount: calcBaseAmountFromUsd({ + usdAmount: amount, + priceUsd: tokenBalance.price, + }), + symbol: tokenBalance.token.symbol, + }); + }), + ), +); + +export const useTokenAmountValue = ( + walletAddress: WalletAccount["address"], +) => { + const tokenAmountValue = useAtomValue( + tokenAmountValueAtom(walletAddress), + ).pipe(Result.getOrElse(() => null)); + + return { + tokenAmountValue, + }; +}; diff --git a/packages/common/src/hooks/use-edit-position.ts b/packages/common/src/hooks/use-edit-position.ts new file mode 100644 index 0000000..aefbd3b --- /dev/null +++ b/packages/common/src/hooks/use-edit-position.ts @@ -0,0 +1,28 @@ +import { useAtomSet, useAtomValue } from "@effect-atom/atom-react"; +import { editSLTPAtom } from "../atoms/edit-position-atoms"; +import { updateLeverageAtom } from "../atoms/position-pending-actions-atom"; + +export const useEditSLTP = () => { + const editTPResult = useAtomValue(editSLTPAtom("takeProfit")); + const editTP = useAtomSet(editSLTPAtom("takeProfit")); + + const editSLResult = useAtomValue(editSLTPAtom("stopLoss")); + const editSL = useAtomSet(editSLTPAtom("stopLoss")); + + return { + editTPResult, + editTP, + editSLResult, + editSL, + }; +}; + +export const useUpdateLeverage = () => { + const updateLeverageResult = useAtomValue(updateLeverageAtom); + const updateLeverage = useAtomSet(updateLeverageAtom); + + return { + updateLeverageResult, + updateLeverage, + }; +}; diff --git a/packages/common/src/hooks/use-order-form.ts b/packages/common/src/hooks/use-order-form.ts new file mode 100644 index 0000000..85fedb4 --- /dev/null +++ b/packages/common/src/hooks/use-order-form.ts @@ -0,0 +1,213 @@ +import { Result, useAtomSet, useAtomValue } from "@effect-atom/atom-react"; +import { Option, Schema } from "effect"; +import { + calculateOrderPercentage, + calculateOrderPositionSize, + getOrderCalculations, + LeverageRangesSchema, + leverageAtom, + limitPriceAtom, + orderFormAtom, + orderSideAtom, + orderTypeAtom, + tpOrSLSettingsAtom, +} from "../atoms/order-form-atoms"; +import { + positionsAtom, + selectedProviderBalancesAtom, +} from "../atoms/portfolio-atoms"; +import type { WalletConnected } from "../domain"; +import type { ApiSchemas, ApiTypes } from "../services"; + +export const useOrderType = () => { + const orderType = useAtomValue(orderTypeAtom); + const setOrderType = useAtomSet(orderTypeAtom); + + return { + orderType, + setOrderType, + }; +}; + +export const useOrderSide = () => { + const orderSide = useAtomValue(orderSideAtom); + const setOrderSide = useAtomSet(orderSideAtom); + + return { + orderSide, + setOrderSide, + }; +}; + +export const useLeverage = ( + leverageRanges: typeof LeverageRangesSchema.Type, +) => { + const leverage = useAtomValue(leverageAtom(leverageRanges)); + const setLeverage = useAtomSet(leverageAtom(leverageRanges)); + + return { + leverage, + setLeverage, + }; +}; + +export const useTPOrSLSettings = () => { + const tpOrSLSettings = useAtomValue(tpOrSLSettingsAtom); + const setTPOrSLSettings = useAtomSet(tpOrSLSettingsAtom); + + return { + tpOrSLSettings, + setTPOrSLSettings, + }; +}; + +export const useLimitPrice = () => { + const limitPrice = useAtomValue(limitPriceAtom); + const setLimitPrice = useAtomSet(limitPriceAtom); + + return { + limitPrice, + setLimitPrice, + }; +}; + +export const useOrderProviderBalance = (wallet: WalletConnected) => { + const providerBalance = useAtomValue( + selectedProviderBalancesAtom(wallet.currentAccount.address), + ).pipe(Result.getOrElse(() => null)); + + return { + providerBalance, + }; +}; + +export const useCurrentPosition = ( + wallet: WalletConnected, + marketId: string, +) => { + const positions = useAtomValue( + positionsAtom(wallet.currentAccount.address), + ).pipe(Result.getOrElse(() => [])); + + const currentPosition = positions.find( + (position) => position.marketId === marketId, + ); + + return { + currentPosition: currentPosition ?? null, + }; +}; + +// Hooks that depend on orderFormAtom +export const useOrderFormSubmit = ( + leverageRanges: typeof LeverageRangesSchema.Type, +) => { + const { form } = useAtomValue(orderFormAtom(leverageRanges)); + const submit = useAtomSet(form.submit); + const submitResult = useAtomValue(form.submit); + + return { + submit, + submitResult, + }; +}; + +export const useOrderAmount = ( + leverageRanges: typeof LeverageRangesSchema.Type, +) => { + const { amountFieldAtom } = useAtomValue(orderFormAtom(leverageRanges)); + const amount = useAtomValue(amountFieldAtom).pipe( + Option.map(Number), + Option.filter((v) => !Number.isNaN(v)), + Option.getOrElse(() => 0), + ); + + return { amount }; +}; + +export const useHandlePercentageChange = ( + wallet: WalletConnected, + leverageRanges: typeof LeverageRangesSchema.Type, +) => { + const { setAmountFieldAtom } = useAtomValue(orderFormAtom(leverageRanges)); + const setAmount = useAtomSet(setAmountFieldAtom); + const { providerBalance } = useOrderProviderBalance(wallet); + const { leverage } = useLeverage(leverageRanges); + + return { + handlePercentageChange: (value: number) => { + if (!providerBalance) return; + + const positionSize = calculateOrderPositionSize( + providerBalance, + leverage, + value, + ); + + setAmount(positionSize.toString()); + }, + }; +}; + +export const useOrderPercentage = ( + wallet: WalletConnected, + leverageRanges: typeof LeverageRangesSchema.Type, +) => { + const { amount } = useOrderAmount(leverageRanges); + const { leverage } = useLeverage(leverageRanges); + + const providerBalance = useAtomValue( + selectedProviderBalancesAtom(wallet.currentAccount.address), + ).pipe(Result.getOrElse(() => null)); + + if (!providerBalance) { + return { + percentage: 0, + }; + } + + const percentage = calculateOrderPercentage( + amount, + leverage, + providerBalance.availableBalance, + ); + + return { + percentage, + }; +}; + +export const useOrderCalculations = ( + market: ApiSchemas.MarketDto, + side: ApiTypes.PositionDtoSide, + leverageRanges: typeof LeverageRangesSchema.Type, +) => { + const { amount } = useOrderAmount(leverageRanges); + const { leverage } = useLeverage(leverageRanges); + + return getOrderCalculations(amount, leverage, market, side); +}; + +// Re-export from order-form-atoms for convenience +export { + LeverageRangesSchema, + ORDER_SLIDER_STOPS, + type OrderSide, + type OrderType, + orderFormAtom, +} from "../atoms/order-form-atoms"; + +// Helper to decode leverage ranges from market +export const decodeLeverageRanges = ( + leverageRange: ApiSchemas.MarketDto["leverageRange"], +) => Schema.decodeSync(LeverageRangesSchema)(leverageRange); + +export const useProviderBalance = (wallet: WalletConnected) => { + const providerBalance = useAtomValue( + selectedProviderBalancesAtom(wallet.currentAccount.address), + ).pipe(Result.getOrElse(() => null)); + + return { + providerBalance, + }; +}; diff --git a/packages/common/src/hooks/use-withdraw-form.ts b/packages/common/src/hooks/use-withdraw-form.ts new file mode 100644 index 0000000..522d9ea --- /dev/null +++ b/packages/common/src/hooks/use-withdraw-form.ts @@ -0,0 +1,180 @@ +import { + Registry, + Result, + useAtomSet, + useAtomValue, +} from "@effect-atom/atom-react"; +import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; +import { Effect, Option, Schema } from "effect"; +import { + actionAtom, + selectedProviderAtom, + selectedProviderBalancesAtom, + walletAtom, +} from "../atoms"; +import { AmountField } from "../components"; +import { isWalletConnected, type WalletAccount } from "../domain"; +import { clampPercent, percentOf, round, valueFromPercent } from "../lib"; +import { ApiClientService, runtimeAtom } from "../services"; + +export const withdrawFormBuilder = FormBuilder.empty + .addField( + "Amount", + Schema.NumberFromString.pipe( + Schema.annotations({ message: () => "Invalid amount" }), + Schema.greaterThan(0, { message: () => "Must be greater than 0" }), + ), + ) + .refineEffect((values) => + Effect.gen(function* () { + const registry = yield* Registry.AtomRegistry; + const wallet = registry + .get(walletAtom) + .pipe(Result.getOrElse(() => null)); + + if (!isWalletConnected(wallet)) { + return yield* Effect.dieMessage("No wallet"); + } + + const providerBalance = registry + .get(selectedProviderBalancesAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!providerBalance) { + return { path: ["Amount"], message: "Missing provider balance" }; + } + + if (providerBalance.availableBalance <= 0) { + return { + path: ["Amount"], + message: "No available balance to withdraw", + }; + } + + if (values.Amount > providerBalance.availableBalance) { + return { + path: ["Amount"], + message: "Insufficient balance", + }; + } + }), + ); + +export type WithdrawFormBuilder = typeof withdrawFormBuilder; + +const withdrawOnSubmit = Effect.gen(function* () { + const client = yield* ApiClientService; + const registry = yield* Registry.AtomRegistry; + + const wallet = registry.get(walletAtom).pipe(Result.getOrElse(() => null)); + + if (!isWalletConnected(wallet)) { + return yield* Effect.dieMessage("No wallet"); + } + + const selectedProvider = registry + .get(selectedProviderAtom) + .pipe(Result.getOrElse(() => null)); + + if (!selectedProvider) { + return yield* Effect.dieMessage("No selected provider"); + } + + const providerBalance = registry + .get(selectedProviderBalancesAtom(wallet.currentAccount.address)) + .pipe(Result.getOrElse(() => null)); + + if (!providerBalance) { + return yield* Effect.dieMessage("No provider balance"); + } + + return { client, wallet, selectedProvider, providerBalance }; +}); + +export const createWithdrawForm = ( + amountFieldComponent: FormReact.FieldComponent, +) => + FormReact.make(withdrawFormBuilder, { + runtime: runtimeAtom, + fields: { Amount: amountFieldComponent }, + onSubmit: (_, { decoded }) => + Effect.gen(function* () { + const { client, wallet, selectedProvider } = yield* withdrawOnSubmit; + + const action = yield* client.ActionsControllerExecuteAction({ + providerId: selectedProvider.id, + address: wallet.currentAccount.address, + action: "withdraw", + args: { + amount: decoded.Amount.toString(), + }, + }); + + const registry = yield* Registry.AtomRegistry; + registry.set(actionAtom, action); + }), + }); + +export const WithdrawForm = createWithdrawForm(AmountField); + +const setAmountFieldAtom = WithdrawForm.setValue(WithdrawForm.fields.Amount); +const amountFieldAtom = WithdrawForm.getFieldValue(WithdrawForm.fields.Amount); + +export const useWithdrawForm = () => { + const submit = useAtomSet(WithdrawForm.submit); + const submitResult = useAtomValue(WithdrawForm.submit); + + return { + submit, + submitResult, + }; +}; + +export const useSetWithdrawAmount = () => { + const setAmount = useAtomSet(setAmountFieldAtom); + + return { + setAmount, + }; +}; + +export const useWithdrawPercentage = ( + walletAddress: WalletAccount["address"], +) => { + const amount = useAtomValue(amountFieldAtom).pipe( + Option.map(Number), + Option.filter((v) => !Number.isNaN(v)), + Option.getOrElse(() => 0), + ); + + const setAmount = useAtomSet(setAmountFieldAtom); + + const availableBalance = useAtomValue( + selectedProviderBalancesAtom(walletAddress), + ).pipe( + Result.value, + Option.map((v) => v.availableBalance), + Option.getOrElse(() => 0), + ); + + const handlePercentageChange = (newPercentage: number) => { + if (newPercentage >= 100) { + return setAmount(availableBalance.toString()); + } + + const amount = valueFromPercent({ + total: availableBalance, + percent: newPercentage, + }); + setAmount(round(amount, 6).toString()); + }; + + const percentage = clampPercent( + percentOf({ part: amount, whole: availableBalance }), + ); + + return { + percentage: Math.round(percentage), + handlePercentageChange, + }; +}; diff --git a/packages/dashboard/src/components/modules/trade/order-form/index.tsx b/packages/dashboard/src/components/modules/trade/order-form/index.tsx index d38510f..8cf26d5 100644 --- a/packages/dashboard/src/components/modules/trade/order-form/index.tsx +++ b/packages/dashboard/src/components/modules/trade/order-form/index.tsx @@ -4,7 +4,11 @@ import { useAtomRef, useAtomValue, } from "@effect-atom/atom-react"; -import { walletAtom } from "@yieldxyz/perps-common/atoms"; +import { + LeverageRangesSchema, + orderFormAtom, + walletAtom, +} from "@yieldxyz/perps-common/atoms"; import { Button, LeverageDialog, @@ -17,6 +21,19 @@ import { isWalletConnected, type WalletConnected, } from "@yieldxyz/perps-common/domain"; +import { + useCurrentPosition, + useHandlePercentageChange, + useLeverage, + useLimitPrice, + useOrderCalculations, + useOrderFormSubmit, + useOrderPercentage, + useOrderSide, + useOrderType, + useProviderBalance, + useTPOrSLSettings, +} from "@yieldxyz/perps-common/hooks"; import { cn, formatAmount, @@ -28,17 +45,6 @@ import type { ApiTypes } from "@yieldxyz/perps-common/services"; import { Schema } from "effect"; import { ChevronDown, Info } from "lucide-react"; import { selectedMarketAtom } from "../../../../atoms/selected-market-atom"; -import { - formAtom, - LeverageRangesSchema, - useCurrentPosition, - useLeverage, - useLimitPrice, - useOrderSide, - useOrderType, - useProviderBalance, - useTPOrSLSettings, -} from "./state"; interface OrderFormProps { className?: string; @@ -212,20 +218,17 @@ function OrderFormContent({ const { providerBalance } = useProviderBalance(wallet); const { currentPosition } = useCurrentPosition(wallet, market.id); - const { - hooks: { - useOrderForm, - useOrderPercentage, - useOrderCalculations, - useHandlePercentageChange, - }, - form: OrderFormComponent, - } = useAtomValue(formAtom(leverageRanges)); - - const { submit, submitResult } = useOrderForm(); - const { handlePercentageChange } = useHandlePercentageChange(wallet); + const { form: OrderFormComponent } = useAtomValue( + orderFormAtom(leverageRanges), + ); + + const { submit, submitResult } = useOrderFormSubmit(leverageRanges); + const { handlePercentageChange } = useHandlePercentageChange( + wallet, + leverageRanges, + ); const { percentage } = useOrderPercentage(wallet, leverageRanges); - const calculations = useOrderCalculations(market, orderSide); + const calculations = useOrderCalculations(market, orderSide, leverageRanges); const maxLeverage = getMaxLeverage(leverageRanges); const currentPrice = market.markPrice; diff --git a/packages/dashboard/src/components/modules/trade/order-form/state.tsx b/packages/dashboard/src/components/modules/trade/order-form/state.tsx deleted file mode 100644 index c3d79bf..0000000 --- a/packages/dashboard/src/components/modules/trade/order-form/state.tsx +++ /dev/null @@ -1,419 +0,0 @@ -import { - Atom, - Registry, - Result, - useAtomSet, - useAtomValue, -} from "@effect-atom/atom-react"; -import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; -import { - actionAtom, - positionsAtom, - selectedProviderAtom, - selectedProviderBalancesAtom, - walletAtom, -} from "@yieldxyz/perps-common/atoms"; -import { - AmountField, - type TPOrSLSettings, -} from "@yieldxyz/perps-common/components"; -import { - isWalletConnected, - type WalletConnected, -} from "@yieldxyz/perps-common/domain"; -import { - calcBaseAmountFromUsd, - calculateMargin, - calculatePositionSize, - clampPercent, - getLiquidationPrice, - getMaxLeverage, - MIN_LEVERAGE, - percentOf, - round, - valueFromPercent, -} from "@yieldxyz/perps-common/lib"; -import { - ApiClientService, - ApiSchemas, - type ApiTypes, - runtimeAtom, -} from "@yieldxyz/perps-common/services"; -import { Number as _Number, Effect, Option, Schema } from "effect"; - -export type OrderType = "market" | "limit"; -export type OrderSide = ApiTypes.PositionDtoSide; - -export const LeverageRangesSchema = Schema.Data( - ApiSchemas.MarketDto.fields.leverageRange, -).pipe(Schema.brand("LeverageRange")); - -// Order Type Atom -const orderTypeAtom = Atom.writable( - () => "market", - (ctx, value) => ctx.setSelf(value), -); - -// Order Side Atom -const orderSideAtom = Atom.writable( - () => "long", - (ctx, value) => ctx.setSelf(value), -); - -// Leverage Atom (family keyed by leverage ranges) -const leverageAtom = Atom.family( - (leverageRanges: typeof LeverageRangesSchema.Type) => - Atom.writable( - () => getMaxLeverage(leverageRanges), - (ctx, value: number) => - ctx.setSelf( - _Number.clamp({ - minimum: MIN_LEVERAGE, - maximum: getMaxLeverage(leverageRanges), - })(value), - ), - ), -); - -// TP/SL Settings Atom -const tpOrSLSettingsAtom = Atom.writable( - () => ({ - takeProfit: { - option: null, - triggerPrice: null, - percentage: null, - }, - stopLoss: { - option: null, - triggerPrice: null, - percentage: null, - }, - }), - (ctx, value) => ctx.setSelf(value), -); - -// Limit Price Atom -const limitPriceAtom = Atom.writable( - () => null, - (ctx, value) => ctx.setSelf(value), -); - -// Hooks -export const useOrderType = () => { - const orderType = useAtomValue(orderTypeAtom); - const setOrderType = useAtomSet(orderTypeAtom); - - return { - orderType, - setOrderType, - }; -}; - -export const useOrderSide = () => { - const orderSide = useAtomValue(orderSideAtom); - const setOrderSide = useAtomSet(orderSideAtom); - - return { - orderSide, - setOrderSide, - }; -}; - -export const useLeverage = ( - leverageRanges: typeof LeverageRangesSchema.Type, -) => { - const leverage = useAtomValue(leverageAtom(leverageRanges)); - const setLeverage = useAtomSet(leverageAtom(leverageRanges)); - - return { - leverage, - setLeverage, - }; -}; - -export const useTPOrSLSettings = () => { - const tpOrSLSettings = useAtomValue(tpOrSLSettingsAtom); - const setTPOrSLSettings = useAtomSet(tpOrSLSettingsAtom); - - return { - tpOrSLSettings, - setTPOrSLSettings, - }; -}; - -export const useLimitPrice = () => { - const limitPrice = useAtomValue(limitPriceAtom); - const setLimitPrice = useAtomSet(limitPriceAtom); - - return { - limitPrice, - setLimitPrice, - }; -}; - -export const useProviderBalance = (wallet: WalletConnected) => { - const providerBalance = useAtomValue( - selectedProviderBalancesAtom(wallet.currentAccount.address), - ).pipe(Result.getOrElse(() => null)); - - return { - providerBalance, - }; -}; - -export const useCurrentPosition = ( - wallet: WalletConnected, - marketId: string, -) => { - const positions = useAtomValue( - positionsAtom(wallet.currentAccount.address), - ).pipe(Result.getOrElse(() => [])); - - const currentPosition = positions.find( - (position) => position.marketId === marketId, - ); - - return { - currentPosition: currentPosition ?? null, - }; -}; - -// Form Atom -export const formAtom = Atom.family( - (leverageRanges: typeof LeverageRangesSchema.Type) => { - const orderFormBuilder = FormBuilder.empty - .addField( - "Amount", - Schema.NumberFromString.pipe( - Schema.annotations({ message: () => "Invalid amount" }), - Schema.greaterThan(0, { message: () => "Must be greater than 0" }), - ), - ) - .refineEffect((values) => - Effect.gen(function* () { - const registry = yield* Registry.AtomRegistry; - const wallet = registry - .get(walletAtom) - .pipe(Result.getOrElse(() => null)); - - if (!isWalletConnected(wallet)) { - return yield* Effect.dieMessage("No wallet"); - } - - const providerBalance = registry - .get(selectedProviderBalancesAtom(wallet.currentAccount.address)) - .pipe(Result.getOrElse(() => null)); - - if (!providerBalance) { - return { path: ["Amount"], message: "Missing provider balance" }; - } - - const leverage = registry.get(leverageAtom(leverageRanges)); - const requiredMargin = calculateMargin({ - positionSize: values.Amount, - leverage, - }); - - if (requiredMargin > providerBalance.availableBalance) { - return { - path: ["Amount"], - message: "Insufficient balance", - }; - } - }), - ); - - const OrderForm = FormReact.make(orderFormBuilder, { - runtime: runtimeAtom, - fields: { Amount: AmountField }, - onSubmit: ( - { - wallet, - market, - side, - }: { - wallet: WalletConnected; - market: ApiSchemas.MarketDto; - side: ApiTypes.PositionDtoSide; - }, - { decoded }, - ) => - Effect.gen(function* () { - const client = yield* ApiClientService; - const registry = yield* Registry.AtomRegistry; - - const selectedProvider = registry - .get(selectedProviderAtom) - .pipe(Result.getOrElse(() => null)); - - if (!selectedProvider) { - return yield* Effect.dieMessage("No selected provider"); - } - - const leverage = registry.get(leverageAtom(leverageRanges)); - const orderType = registry.get(orderTypeAtom); - const tpOrSLSettings = registry.get(tpOrSLSettingsAtom); - - const stopLossPrice: ApiTypes.ArgumentsDto["stopLossPrice"] = - tpOrSLSettings.stopLoss.triggerPrice && - tpOrSLSettings.stopLoss.option !== null - ? tpOrSLSettings.stopLoss.triggerPrice - : undefined; - - const takeProfitPrice: ApiTypes.ArgumentsDto["takeProfitPrice"] = - tpOrSLSettings.takeProfit.triggerPrice && - tpOrSLSettings.takeProfit.option !== null - ? tpOrSLSettings.takeProfit.triggerPrice - : undefined; - - const limitPrice = - orderType === "limit" ? registry.get(limitPriceAtom) : undefined; - - const action = yield* client.ActionsControllerExecuteAction({ - providerId: selectedProvider.id, - address: wallet.currentAccount.address, - action: "open", - args: { - marketId: market.id, - side, - size: decoded.Amount.toString(), - marginMode: "isolated", - ...(stopLossPrice && { stopLossPrice }), - ...(takeProfitPrice && { takeProfitPrice }), - ...(leverage && { leverage }), - ...(limitPrice && { limitPrice: limitPrice }), - }, - }); - - registry.set(actionAtom, action); - }), - }); - - const useOrderForm = () => { - const submit = useAtomSet(OrderForm.submit); - const submitResult = useAtomValue(OrderForm.submit); - - return { - submit, - submitResult, - }; - }; - - const setAmountFieldAtom = OrderForm.setValue(OrderForm.fields.Amount); - const amountFieldAtom = OrderForm.getFieldValue(OrderForm.fields.Amount); - - const useHandlePercentageChange = (wallet: WalletConnected) => { - const setAmount = useAtomSet(setAmountFieldAtom); - const { providerBalance } = useProviderBalance(wallet); - const { leverage } = useLeverage(leverageRanges); - - return { - handlePercentageChange: (value: number) => { - if (!providerBalance) return; - - const clampedValue = clampPercent(value); - const marginToUse = valueFromPercent({ - total: providerBalance.availableBalance, - percent: clampedValue, - }); - const positionSize = calculatePositionSize({ - margin: marginToUse, - leverage, - }); - - setAmount(round(positionSize).toString()); - }, - }; - }; - - const useOrderAmount = () => { - const amount = useAtomValue(amountFieldAtom).pipe( - Option.map(Number), - Option.filter((v) => !Number.isNaN(v)), - Option.getOrElse(() => 0), - ); - - return { amount }; - }; - - const useOrderPercentage = ( - wallet: WalletConnected, - leverageRanges: typeof LeverageRangesSchema.Type, - ) => { - const amount = useAtomValue(amountFieldAtom).pipe( - Option.map(Number), - Option.filter((v) => !Number.isNaN(v)), - Option.getOrElse(() => 0), - ); - - const { leverage } = useLeverage(leverageRanges); - - const providerBalance = useAtomValue( - selectedProviderBalancesAtom(wallet.currentAccount.address), - ).pipe(Result.getOrElse(() => null)); - - if (!providerBalance) { - return { - percentage: 0, - }; - } - - const margin = calculateMargin({ positionSize: amount, leverage }); - const percentage = clampPercent( - percentOf({ part: margin, whole: providerBalance.availableBalance }), - ); - - return { - percentage: Math.round(percentage), - }; - }; - - const useOrderCalculations = ( - market: ApiSchemas.MarketDto, - side: ApiTypes.PositionDtoSide, - ) => { - const { amount } = useOrderAmount(); - const { leverage } = useLeverage( - Schema.decodeSync(LeverageRangesSchema)(market.leverageRange), - ); - - const cryptoAmount = calcBaseAmountFromUsd({ - usdAmount: amount, - priceUsd: market.markPrice, - }); - - const liquidationPrice = getLiquidationPrice({ - currentPrice: market.markPrice, - leverage, - side, - }); - - const margin = calculateMargin({ positionSize: amount, leverage }); - - const feeRate = market.takerFee ? Number.parseFloat(market.takerFee) : 0; - const fees = amount * feeRate; - - return { - margin, - amount, - cryptoAmount, - liquidationPrice, - fees, - leverage, - }; - }; - - const hooks = { - useOrderForm, - useOrderAmount, - useOrderPercentage, - useOrderCalculations, - useHandlePercentageChange, - }; - - return Atom.readable(() => ({ - hooks, - form: OrderForm, - })); - }, -); diff --git a/packages/dashboard/src/components/molecules/header/deposit/deposit-dialog.tsx b/packages/dashboard/src/components/molecules/header/deposit/deposit-dialog.tsx index 23ef27c..d4f3fe3 100644 --- a/packages/dashboard/src/components/molecules/header/deposit/deposit-dialog.tsx +++ b/packages/dashboard/src/components/molecules/header/deposit/deposit-dialog.tsx @@ -15,6 +15,10 @@ import type { WalletAccount, WalletConnected, } from "@yieldxyz/perps-common/domain"; +import { + useSelectedTokenBalance, + useTokenBalances, +} from "@yieldxyz/perps-common/hooks"; import { formatSnakeCase, formatTokenAmount, @@ -27,8 +31,6 @@ import { useDepositForm, useProviders, useSelectedChain, - useSelectedTokenBalance, - useTokenBalances, } from "./state"; interface DepositDialogProps { @@ -68,7 +70,7 @@ function DepositDialogContent({ wallet }: DepositDialogContentProps) { const { submit, submitResult } = useDepositForm(); const handleSubmit = () => { - submit({ wallet }); + submit(); }; return ( diff --git a/packages/dashboard/src/components/molecules/header/deposit/state.tsx b/packages/dashboard/src/components/molecules/header/deposit/state.tsx index c02700d..cfb3a13 100644 --- a/packages/dashboard/src/components/molecules/header/deposit/state.tsx +++ b/packages/dashboard/src/components/molecules/header/deposit/state.tsx @@ -1,29 +1,24 @@ import { Atom, - Registry, Result, useAtomSet, useAtomValue, } from "@effect-atom/atom-react"; -import { FormBuilder, FormReact } from "@lucas-barake/effect-form-react"; +import type { FormReact } from "@lucas-barake/effect-form-react"; +import { EvmNetworks } from "@stakekit/common"; import { - actionAtom, - moralisTokenBalancesAtom, selectedProviderAtom, - type TokenBalances, - walletAtom, type yieldApiNetworkToMoralisChain, } from "@yieldxyz/perps-common/atoms"; import { Text } from "@yieldxyz/perps-common/components"; -import { - isArbUsdcToken, - isEthNativeToken, - isWalletConnected, - makeToken, - type TokenBalance, - type WalletAccount, - type WalletConnected, +import type { + TokenBalance, + WalletAccount, } from "@yieldxyz/perps-common/domain"; +import { + createDepositForm, + selectedTokenBalanceAtom, +} from "@yieldxyz/perps-common/hooks"; import { calcBaseAmountFromUsd, clampPercent, @@ -32,43 +27,19 @@ import { round, valueFromPercent, } from "@yieldxyz/perps-common/lib"; -import { - ApiClientService, - type ApiTypes, - runtimeAtom, -} from "@yieldxyz/perps-common/services"; -import { - Array as _Array, - Effect, - Option, - Predicate, - Record, - Schema, -} from "effect"; +import { type ApiTypes, runtimeAtom } from "@yieldxyz/perps-common/services"; +import { Effect, Option } from "effect"; // Selected chain atom (network) type ChainKey = keyof typeof yieldApiNetworkToMoralisChain; -const initialChainAtom = Atom.make("ethereum"); +const initialChainAtom = Atom.make(EvmNetworks.Ethereum); export const selectedChainAtom = Atom.writable( (ctx) => ctx.get(initialChainAtom), (_ctx, value: ChainKey) => _ctx.setSelf(value), ); -// Selected token balance atom based on wallet address -const selectedTokenBalanceAtom = Atom.family( - (walletAddress: WalletAccount["address"]) => - Atom.writable( - (ctx) => - ctx.get(moralisTokenBalancesAtom(walletAddress)).pipe( - Result.map((res) => _Array.head(res.ethereum)), - Result.map(Option.getOrNull), - ), - (ctx, value: TokenBalance) => ctx.setSelf(Result.success(value)), - ), -); - // Hooks for using atoms in components export const useProviders = (): { selectedProvider: ApiTypes.ProviderDto | null; @@ -95,84 +66,6 @@ export const useSelectedChain = () => { }; }; -export const useTokenBalances = (walletAddress: WalletAccount["address"]) => { - const tokenBalances = useAtomValue( - moralisTokenBalancesAtom(walletAddress), - ).pipe(Result.getOrElse(() => Record.empty() as TokenBalances)); - - return { - tokenBalances, - }; -}; - -export const useSelectedTokenBalance = ( - walletAddress: WalletAccount["address"], -) => { - const selectedTokenBalance = useAtomValue( - selectedTokenBalanceAtom(walletAddress), - ).pipe(Result.getOrElse(() => null)); - const setSelectedTokenBalance = useAtomSet( - selectedTokenBalanceAtom(walletAddress), - ); - const setAmount = useAtomSet(setAmountFieldAtom); - - const handleSelectTokenBalance = (tokenBalance: TokenBalance) => { - setSelectedTokenBalance(tokenBalance); - setAmount("0"); - }; - - return { - selectedTokenBalance, - handleSelectTokenBalance, - }; -}; - -// Form builder for deposit -export const depositFormBuilder = FormBuilder.empty - .addField( - "Amount", - Schema.NumberFromString.pipe( - Schema.annotations({ message: () => "Invalid amount" }), - Schema.greaterThan(0, { message: () => "Must be greater than 0" }), - ), - ) - .refineEffect((values) => - Effect.gen(function* () { - const registry = yield* Registry.AtomRegistry; - const wallet = registry - .get(walletAtom) - .pipe(Result.getOrElse(() => null)); - - if (!isWalletConnected(wallet)) { - yield* Effect.logWarning("No wallet found"); - return; - } - - const tokenBalance = registry - .get(selectedTokenBalanceAtom(wallet.currentAccount.address)) - .pipe(Result.getOrElse(() => null)); - - if (!tokenBalance) { - return { path: ["Amount"], message: "Missing token balance" }; - } - - const cryptoAmount = calcBaseAmountFromUsd({ - usdAmount: values.Amount, - priceUsd: tokenBalance.price, - }); - - const usdMin = isArbUsdcToken(makeToken(tokenBalance.token)) ? 5 : 10; - - if (values.Amount < usdMin) { - return { path: ["Amount"], message: `Minimum deposit is $${usdMin}` }; - } - - if (Number(tokenBalance.amount) < cryptoAmount) { - return { path: ["Amount"], message: "Insufficient balance" }; - } - }), - ); - // Custom amount field component that matches the Figma design const DepositAmountField: FormReact.FieldComponent = ({ field }) => { const onChange: (typeof field)["onChange"] = (newValue) => { @@ -234,53 +127,7 @@ const DepositAmountField: FormReact.FieldComponent = ({ field }) => { ); }; -export const DepositForm = FormReact.make(depositFormBuilder, { - runtime: runtimeAtom, - fields: { Amount: DepositAmountField }, - onSubmit: ({ wallet }: { wallet: WalletConnected }, { decoded }) => - Effect.gen(function* () { - const client = yield* ApiClientService; - const registry = yield* Registry.AtomRegistry; - - const selectedTokenBalance = registry - .get(selectedTokenBalanceAtom(wallet.currentAccount.address)) - .pipe(Result.getOrElse(() => null)); - - if (!selectedTokenBalance) { - return yield* Effect.dieMessage("No selected token balance"); - } - - const selectedProvider = registry - .get(selectedProviderAtom) - .pipe(Result.getOrElse(() => null)); - - if (!selectedProvider) { - return yield* Effect.dieMessage("No selected provider"); - } - - const cryptoAmount = calcBaseAmountFromUsd({ - usdAmount: decoded.Amount, - priceUsd: selectedTokenBalance.price, - }); - - const action = yield* client.ActionsControllerExecuteAction({ - providerId: selectedProvider.id, - address: wallet.currentAccount.address, - action: "fund", - args: { - amount: cryptoAmount.toString(), - fromToken: { - network: selectedTokenBalance.token.network, - ...(!isEthNativeToken(makeToken(selectedTokenBalance.token)) && { - address: selectedTokenBalance.token.address, - }), - }, - }, - }); - - registry.set(actionAtom, action); - }), -}); +export const DepositForm = createDepositForm(DepositAmountField); const amountAtom = DepositForm.getFieldValue(DepositForm.fields.Amount); const setAmountFieldAtom = DepositForm.setValue(DepositForm.fields.Amount); @@ -295,7 +142,29 @@ export const useDepositForm = () => { }; }; -export const useDepositPercentage = ( +export const useDashboardSelectedTokenBalance = ( + walletAddress: WalletAccount["address"], +) => { + const selectedTokenBalance = useAtomValue( + selectedTokenBalanceAtom(walletAddress), + ).pipe(Result.getOrElse(() => null)); + const setSelectedTokenBalance = useAtomSet( + selectedTokenBalanceAtom(walletAddress), + ); + const setAmount = useAtomSet(setAmountFieldAtom); + + const handleSelectTokenBalance = (tokenBalance: TokenBalance) => { + setSelectedTokenBalance(tokenBalance); + setAmount("0"); + }; + + return { + selectedTokenBalance, + handleSelectTokenBalance, + }; +}; + +export const useDashboardDepositPercentage = ( walletAddress: WalletAccount["address"], ) => { const amount = useAtomValue(amountAtom).pipe( @@ -306,14 +175,11 @@ export const useDepositPercentage = ( const setAmount = useAtomSet(setAmountFieldAtom); - const availableBalanceUsd = useAtomValue( - selectedTokenBalanceAtom(walletAddress), - ).pipe( - Result.value, - Option.filter(Predicate.isNotNull), - Option.map((balance) => Number(balance.amount) * balance.price), - Option.getOrElse(() => 0), - ); + const { selectedTokenBalance } = + useDashboardSelectedTokenBalance(walletAddress); + const availableBalanceUsd = selectedTokenBalance + ? Number(selectedTokenBalance.amount) * selectedTokenBalance.price + : 0; const percentage = clampPercent( percentOf({ part: amount, whole: availableBalanceUsd }), @@ -324,11 +190,11 @@ export const useDepositPercentage = ( return setAmount(availableBalanceUsd.toString()); } - const amount = valueFromPercent({ + const newAmount = valueFromPercent({ total: availableBalanceUsd, percent: newPercentage, }); - setAmount(round(amount).toString()); + setAmount(round(newAmount).toString()); }; return { @@ -368,7 +234,7 @@ const tokenAmountValueAtom = Atom.family( ), ); -export const useTokenAmountValue = ( +export const useDashboardTokenAmountValue = ( walletAddress: WalletAccount["address"], ) => { const tokenAmountValue = useAtomValue( diff --git a/packages/dashboard/src/components/molecules/header/index.tsx b/packages/dashboard/src/components/molecules/header/index.tsx index 4c54a2c..cbf42b3 100644 --- a/packages/dashboard/src/components/molecules/header/index.tsx +++ b/packages/dashboard/src/components/molecules/header/index.tsx @@ -83,7 +83,7 @@ function HeaderWalletSection() {