From e1d593656c71f0a803fbf9c586a53954c39e30cd Mon Sep 17 00:00:00 2001 From: Gaubee Date: Wed, 7 Jan 2026 13:15:46 +0800 Subject: [PATCH 01/53] docs: refactor white-book into technical library structure (V11) - Reorganize docs into 8 books (Manifesto, Kernel, Drivers, UI, Wallet, DApp, Shell, DevOps) - Implement 'Deep-Atomic Hierarchy' for better AI context retrieval - Map documentation structure to codebase architecture (WebOS paradigm) --- docs/white-book/00-Manifesto/01-Vision.md | 25 + docs/white-book/00-Manifesto/02-Tech-Stack.md | 30 + .../00-Manifesto/03-Architecture.md | 55 ++ .../white-book/00-Manifesto/04-Get-Started.md | 36 + docs/white-book/00-Manifesto/05-Workflows.md | 24 + docs/white-book/00-Manifesto/06-Guidelines.md | 31 + docs/white-book/00-Manifesto/README.md | 18 + .../best-practices.md" | 33 - .../00-\345\277\205\350\257\273/index.md" | 145 ---- docs/white-book/01-Kernel-Ref/00-Overview.md | 38 + .../01-Process/01-State-Machine.md | 42 + .../01-Kernel-Ref/01-Process/02-Scheduling.md | 22 + .../02-Window/01-Presentation-Model.md | 25 + .../02-Window/02-FLIP-Animation.md | 20 + .../03-Sandbox/01-Iframe-Manager.md | 25 + .../03-Sandbox/02-BioBridge-Protocol.md | 40 + docs/white-book/01-Kernel-Ref/README.md | 18 + .../index.md" | 164 ---- .../index.md" | 260 ------ .../index.md" | 456 ---------- .../index.md" | 56 -- docs/white-book/02-Driver-Ref/00-Overview.md | 49 ++ .../01-Protocol/01-Provider-Interface.md | 55 ++ .../02-Driver-Ref/02-EVM/01-RPC-Provider.md | 21 + .../02-EVM/02-Etherscan-Provider.md | 20 + docs/white-book/02-Driver-Ref/README.md | 18 + .../index.md" | 359 -------- .../dark-mode.md" | 162 ---- .../index.md" | 374 -------- .../theme-colors.md" | 345 -------- .../index.md" | 302 ------- .../index.md" | 56 -- docs/white-book/03-UI-Ref/00-Overview.md | 18 + .../03-UI-Ref/01-Foundation/01-Colors.md | 31 + .../03-UI-Ref/02-Primitives/Button.md | 31 + .../03-UI-Ref/03-Composites/WalletCard.md | 27 + docs/white-book/03-UI-Ref/README.md | 16 + .../index.md" | 290 ------- .../index.md" | 241 ----- .../index.md" | 290 ------- .../index.md" | 331 ------- .../index.md" | 289 ------ .../index.md" | 394 --------- .../index.md" | 282 ------ .../index.md" | 430 --------- .../index.md" | 76 -- .../api-providers-status.md" | 59 -- .../index.md" | 282 ------ .../IAssetService.md" | 161 ---- .../IChainService.md" | 202 ----- .../IIdentityService.md" | 156 ---- .../IStakingService.md" | 226 ----- .../ITransactionService.md" | 234 ----- .../index.md" | 80 -- .../02-web3-adapters.md" | 209 ----- .../index.md" | 424 --------- .../IBiometricService.md" | 164 ---- .../ICameraService.md" | 201 ----- .../IClipboardService.md" | 113 --- .../IDeepLinkService.md" | 220 ----- .../IHapticService.md" | 177 ---- .../IShareService.md" | 153 ---- .../IStorageService.md" | 158 ---- .../index.md" | 87 -- .../index.md" | 294 ------- .../index.md" | 366 -------- .../index.md" | 821 ------------------ .../index.md" | 302 ------- .../index.md" | 347 -------- .../index.md" | 296 ------- .../index.md" | 111 --- .../Button.md" | 158 ---- .../Card.md" | 146 ---- .../Dialog.md" | 165 ---- .../Input.md" | 169 ---- .../Sheet.md" | 238 ----- .../Toast.md" | 172 ---- .../index.md" | 60 -- .../AddressDisplay.md" | 118 --- .../AmountDisplay.md" | 262 ------ .../AnimatedNumber.md" | 164 ---- .../Contact.md" | 189 ---- .../QRCode.md" | 128 --- .../Scanner.md" | 222 ----- .../TokenIcon.md" | 130 --- .../index.md" | 27 - .../ChainSelector.md" | 145 ---- .../TransactionItem.md" | 166 ---- .../WalletCard.md" | 243 ------ .../WalletConfig.md" | 269 ------ .../index.md" | 26 - .../AppLayout.md" | 134 --- .../Header.md" | 152 ---- .../TabBar.md" | 146 ---- .../index.md" | 19 - .../index.md" | 330 ------- .../index.md" | 297 ------- .../index.md" | 106 --- .../index.md" | 267 ------ .../index.md" | 422 --------- .../03-DWEB\346\216\210\346\235\203/index.md" | 382 -------- .../index.md" | 240 ----- .../index.md" | 56 -- .../index.md" | 237 ----- .../index.md" | 113 --- .../index.md" | 29 - .../index.md" | 188 ---- .../index.md" | 218 ----- .../e2e-best-practices.md" | 340 -------- .../index.md" | 129 --- .../storybook-visual-testing.md" | 349 -------- .../index.md" | 250 ------ .../index.md" | 316 ------- .../index.md" | 337 ------- .../index.md" | 37 - .../index.md" | 251 ------ .../index.md" | 87 -- .../index.md" | 349 -------- .../index.md" | 329 ------- .../index.md" | 25 - .../white-book/10-Wallet-Guide/00-Overview.md | 25 + .../01-Account-System/01-Key-Derivation.md | 24 + .../02-Asset-Management/01-Balance-Query.md | 28 + docs/white-book/10-Wallet-Guide/README.md | 16 + ...66\346\236\204\350\256\276\350\256\241.md" | 132 --- ...00\345\217\221\346\214\207\345\215\227.md" | 299 ------- ...13\345\272\217\345\274\200\345\217\221.md" | 221 ----- ...42\351\230\205\347\263\273\347\273\237.md" | 199 ----- ...36\347\216\260\347\212\266\346\200\201.md" | 138 --- .../index.md" | 24 - docs/white-book/11-DApp-Guide/00-Overview.md | 14 + .../01-Runtime-Env/02-Miniapp-Manifest.md | 38 + docs/white-book/11-DApp-Guide/README.md | 14 + docs/white-book/12-Shell-Guide/00-Overview.md | 37 + .../01-Navigation/01-Router-Config.md | 34 + docs/white-book/12-Shell-Guide/README.md | 14 + .../90-DevOps/03-Release/02-Deployment.md | 25 + docs/white-book/90-DevOps/README.md | 16 + docs/white-book/index.md | 205 +---- .../index.md" | 129 --- .../index.md" | 373 -------- .../index.md" | 403 --------- .../D-API\345\217\202\350\200\203/index.md" | 513 ----------- .../index.md" | 353 -------- .../index.md" | 246 ------ .../index.md" | 278 ------ .../index.md" | 106 --- .../index.md" | 243 ------ .../\351\231\204\345\275\225/index.md" | 25 - 149 files changed, 1062 insertions(+), 24355 deletions(-) create mode 100644 docs/white-book/00-Manifesto/01-Vision.md create mode 100644 docs/white-book/00-Manifesto/02-Tech-Stack.md create mode 100644 docs/white-book/00-Manifesto/03-Architecture.md create mode 100644 docs/white-book/00-Manifesto/04-Get-Started.md create mode 100644 docs/white-book/00-Manifesto/05-Workflows.md create mode 100644 docs/white-book/00-Manifesto/06-Guidelines.md create mode 100644 docs/white-book/00-Manifesto/README.md delete mode 100644 "docs/white-book/00-\345\277\205\350\257\273/best-practices.md" delete mode 100644 "docs/white-book/00-\345\277\205\350\257\273/index.md" create mode 100644 docs/white-book/01-Kernel-Ref/00-Overview.md create mode 100644 docs/white-book/01-Kernel-Ref/01-Process/01-State-Machine.md create mode 100644 docs/white-book/01-Kernel-Ref/01-Process/02-Scheduling.md create mode 100644 docs/white-book/01-Kernel-Ref/02-Window/01-Presentation-Model.md create mode 100644 docs/white-book/01-Kernel-Ref/02-Window/02-FLIP-Animation.md create mode 100644 docs/white-book/01-Kernel-Ref/03-Sandbox/01-Iframe-Manager.md create mode 100644 docs/white-book/01-Kernel-Ref/03-Sandbox/02-BioBridge-Protocol.md create mode 100644 docs/white-book/01-Kernel-Ref/README.md delete mode 100644 "docs/white-book/01-\344\272\247\345\223\201\347\257\207/01-\344\272\247\345\223\201\346\204\277\346\231\257/index.md" delete mode 100644 "docs/white-book/01-\344\272\247\345\223\201\347\257\207/02-\347\224\250\346\210\267\347\224\273\345\203\217/index.md" delete mode 100644 "docs/white-book/01-\344\272\247\345\223\201\347\257\207/03-\347\224\250\346\210\267\346\225\205\344\272\213/index.md" delete mode 100644 "docs/white-book/01-\344\272\247\345\223\201\347\257\207/index.md" create mode 100644 docs/white-book/02-Driver-Ref/00-Overview.md create mode 100644 docs/white-book/02-Driver-Ref/01-Protocol/01-Provider-Interface.md create mode 100644 docs/white-book/02-Driver-Ref/02-EVM/01-RPC-Provider.md create mode 100644 docs/white-book/02-Driver-Ref/02-EVM/02-Etherscan-Provider.md create mode 100644 docs/white-book/02-Driver-Ref/README.md delete mode 100644 "docs/white-book/02-\350\256\276\350\256\241\347\257\207/01-\344\272\244\344\272\222\350\256\276\350\256\241/index.md" delete mode 100644 "docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/dark-mode.md" delete mode 100644 "docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/index.md" delete mode 100644 "docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/theme-colors.md" delete mode 100644 "docs/white-book/02-\350\256\276\350\256\241\347\257\207/03-\350\256\276\350\256\241\345\216\237\345\210\231/index.md" delete mode 100644 "docs/white-book/02-\350\256\276\350\256\241\347\257\207/index.md" create mode 100644 docs/white-book/03-UI-Ref/00-Overview.md create mode 100644 docs/white-book/03-UI-Ref/01-Foundation/01-Colors.md create mode 100644 docs/white-book/03-UI-Ref/02-Primitives/Button.md create mode 100644 docs/white-book/03-UI-Ref/03-Composites/WalletCard.md create mode 100644 docs/white-book/03-UI-Ref/README.md delete mode 100644 "docs/white-book/03-\346\236\266\346\236\204\347\257\207/01-\346\212\200\346\234\257\351\200\211\345\236\213/index.md" delete mode 100644 "docs/white-book/03-\346\236\266\346\236\204\347\257\207/02-\347\263\273\347\273\237\346\236\266\346\236\204/index.md" delete mode 100644 "docs/white-book/03-\346\236\266\346\236\204\347\257\207/03-\345\257\274\350\210\252\347\263\273\347\273\237/index.md" delete mode 100644 "docs/white-book/03-\346\236\266\346\236\204\347\257\207/04-\347\212\266\346\200\201\347\256\241\347\220\206/index.md" delete mode 100644 "docs/white-book/03-\346\236\266\346\236\204\347\257\207/05-\347\246\273\347\272\277\350\203\275\345\212\233/index.md" delete mode 100644 "docs/white-book/03-\346\236\266\346\236\204\347\257\207/06-\351\224\231\350\257\257\345\244\204\347\220\206/index.md" delete mode 100644 "docs/white-book/03-\346\236\266\346\236\204\347\257\207/07-\346\200\247\350\203\275\351\242\204\347\256\227/index.md" delete mode 100644 "docs/white-book/03-\346\236\266\346\236\204\347\257\207/08-Amount\347\261\273\345\236\213\347\263\273\347\273\237/index.md" delete mode 100644 "docs/white-book/03-\346\236\266\346\236\204\347\257\207/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/api-providers-status.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IAssetService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IChainService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IIdentityService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IStakingService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/ITransactionService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/\345\256\236\347\216\260\350\247\204\350\214\203/02-web3-adapters.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/\345\256\236\347\216\260\350\247\204\350\214\203/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IBiometricService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/ICameraService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IClipboardService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IDeepLinkService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IHapticService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IShareService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IStorageService.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/04-\344\272\213\344\273\266\347\263\273\347\273\237/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/05-Mock\346\234\215\345\212\241\350\247\204\350\214\203/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/06-\346\234\215\345\212\241\345\256\232\344\271\211\347\263\273\347\273\237/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/07-\351\223\276\351\205\215\347\275\256\347\256\241\347\220\206/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/08-\351\222\261\345\214\205\346\225\260\346\215\256\345\255\230\345\202\250/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/09-\350\201\224\347\263\273\344\272\272\346\234\215\345\212\241/index.md" delete mode 100644 "docs/white-book/04-\346\234\215\345\212\241\347\257\207/index.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Button.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Card.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Dialog.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Input.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Sheet.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Toast.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/index.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/AddressDisplay.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/AmountDisplay.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/AnimatedNumber.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/Contact.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/QRCode.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/Scanner.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/TokenIcon.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/index.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/03-\351\222\261\345\214\205\347\273\204\344\273\266/ChainSelector.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/03-\351\222\261\345\214\205\347\273\204\344\273\266/TransactionItem.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/03-\351\222\261\345\214\205\347\273\204\344\273\266/WalletCard.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/03-\351\222\261\345\214\205\347\273\204\344\273\266/WalletConfig.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/03-\351\222\261\345\214\205\347\273\204\344\273\266/index.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/04-\345\270\203\345\261\200\347\273\204\344\273\266/AppLayout.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/04-\345\270\203\345\261\200\347\273\204\344\273\266/Header.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/04-\345\270\203\345\261\200\347\273\204\344\273\266/TabBar.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/04-\345\270\203\345\261\200\347\273\204\344\273\266/index.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/05-\350\241\250\345\215\225\347\263\273\347\273\237/index.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/06-\344\272\244\344\272\222\350\247\204\350\214\203/index.md" delete mode 100644 "docs/white-book/05-\347\273\204\344\273\266\347\257\207/index.md" delete mode 100644 "docs/white-book/06-\345\256\211\345\205\250\347\257\207/01-\345\257\206\351\222\245\347\256\241\347\220\206/index.md" delete mode 100644 "docs/white-book/06-\345\256\211\345\205\250\347\257\207/02-\350\272\253\344\273\275\350\256\244\350\257\201/index.md" delete mode 100644 "docs/white-book/06-\345\256\211\345\205\250\347\257\207/03-DWEB\346\216\210\346\235\203/index.md" delete mode 100644 "docs/white-book/06-\345\256\211\345\205\250\347\257\207/04-\345\256\211\345\205\250\345\256\241\350\256\241\346\270\205\345\215\225/index.md" delete mode 100644 "docs/white-book/06-\345\256\211\345\205\250\347\257\207/index.md" delete mode 100644 "docs/white-book/07-\345\233\275\351\231\205\345\214\226\347\257\207/01-\345\244\232\350\257\255\350\250\200\346\224\257\346\214\201/index.md" delete mode 100644 "docs/white-book/07-\345\233\275\351\231\205\345\214\226\347\257\207/02-\346\234\254\345\234\260\345\214\226\350\247\204\350\214\203/index.md" delete mode 100644 "docs/white-book/07-\345\233\275\351\231\205\345\214\226\347\257\207/index.md" delete mode 100644 "docs/white-book/08-\346\265\213\350\257\225\347\257\207/01-\346\265\213\350\257\225\347\255\226\347\225\245/index.md" delete mode 100644 "docs/white-book/08-\346\265\213\350\257\225\347\257\207/02-Vitest\351\205\215\347\275\256/index.md" delete mode 100644 "docs/white-book/08-\346\265\213\350\257\225\347\257\207/03-Playwright\351\205\215\347\275\256/e2e-best-practices.md" delete mode 100644 "docs/white-book/08-\346\265\213\350\257\225\347\257\207/03-Playwright\351\205\215\347\275\256/index.md" delete mode 100644 "docs/white-book/08-\346\265\213\350\257\225\347\257\207/03-Playwright\351\205\215\347\275\256/storybook-visual-testing.md" delete mode 100644 "docs/white-book/08-\346\265\213\350\257\225\347\257\207/04-\346\200\247\350\203\275\346\265\213\350\257\225/index.md" delete mode 100644 "docs/white-book/08-\346\265\213\350\257\225\347\257\207/05-\345\256\211\345\205\250\346\265\213\350\257\225/index.md" delete mode 100644 "docs/white-book/08-\346\265\213\350\257\225\347\257\207/06-\346\265\213\350\257\225\347\275\221\347\273\234/index.md" delete mode 100644 "docs/white-book/08-\346\265\213\350\257\225\347\257\207/index.md" delete mode 100644 "docs/white-book/09-\351\203\250\347\275\262\347\257\207/01-\346\236\204\345\273\272\351\205\215\347\275\256/index.md" delete mode 100644 "docs/white-book/09-\351\203\250\347\275\262\347\257\207/02-\345\217\221\345\270\203\346\265\201\347\250\213/index.md" delete mode 100644 "docs/white-book/09-\351\203\250\347\275\262\347\257\207/03-\346\225\260\346\215\256\350\277\201\347\247\273/index.md" delete mode 100644 "docs/white-book/09-\351\203\250\347\275\262\347\257\207/04-\347\233\221\346\216\247\345\221\212\350\255\246/index.md" delete mode 100644 "docs/white-book/09-\351\203\250\347\275\262\347\257\207/index.md" create mode 100644 docs/white-book/10-Wallet-Guide/00-Overview.md create mode 100644 docs/white-book/10-Wallet-Guide/01-Account-System/01-Key-Derivation.md create mode 100644 docs/white-book/10-Wallet-Guide/02-Asset-Management/01-Balance-Query.md create mode 100644 docs/white-book/10-Wallet-Guide/README.md delete mode 100644 "docs/white-book/10-\347\224\237\346\200\201\347\257\207/01-\346\236\266\346\236\204\350\256\276\350\256\241.md" delete mode 100644 "docs/white-book/10-\347\224\237\346\200\201\347\257\207/02-BioSDK\345\274\200\345\217\221\346\214\207\345\215\227.md" delete mode 100644 "docs/white-book/10-\347\224\237\346\200\201\347\257\207/03-\345\260\217\347\250\213\345\272\217\345\274\200\345\217\221.md" delete mode 100644 "docs/white-book/10-\347\224\237\346\200\201\347\257\207/04-\350\256\242\351\230\205\347\263\273\347\273\237.md" delete mode 100644 "docs/white-book/10-\347\224\237\346\200\201\347\257\207/05-\345\256\236\347\216\260\347\212\266\346\200\201.md" delete mode 100644 "docs/white-book/10-\347\224\237\346\200\201\347\257\207/index.md" create mode 100644 docs/white-book/11-DApp-Guide/00-Overview.md create mode 100644 docs/white-book/11-DApp-Guide/01-Runtime-Env/02-Miniapp-Manifest.md create mode 100644 docs/white-book/11-DApp-Guide/README.md create mode 100644 docs/white-book/12-Shell-Guide/00-Overview.md create mode 100644 docs/white-book/12-Shell-Guide/01-Navigation/01-Router-Config.md create mode 100644 docs/white-book/12-Shell-Guide/README.md create mode 100644 docs/white-book/90-DevOps/03-Release/02-Deployment.md create mode 100644 docs/white-book/90-DevOps/README.md delete mode 100644 "docs/white-book/\351\231\204\345\275\225/A-\346\234\257\350\257\255\350\241\250/index.md" delete mode 100644 "docs/white-book/\351\231\204\345\275\225/B-\351\223\276\347\275\221\347\273\234\345\210\227\350\241\250/index.md" delete mode 100644 "docs/white-book/\351\231\204\345\275\225/C-mpay\350\277\201\347\247\273\346\214\207\345\215\227/index.md" delete mode 100644 "docs/white-book/\351\231\204\345\275\225/D-API\345\217\202\350\200\203/index.md" delete mode 100644 "docs/white-book/\351\231\204\345\275\225/E-\347\212\266\346\200\201\346\234\272\350\247\204\350\214\203/index.md" delete mode 100644 "docs/white-book/\351\231\204\345\275\225/F-\350\276\271\347\225\214\346\235\241\344\273\266\347\233\256\345\275\225/index.md" delete mode 100644 "docs/white-book/\351\231\204\345\275\225/G-\347\275\221\347\273\234\346\225\205\351\232\234\347\237\251\351\230\265/index.md" delete mode 100644 "docs/white-book/\351\231\204\345\275\225/H-\345\274\200\345\217\221\350\247\204\350\214\203/index.md" delete mode 100644 "docs/white-book/\351\231\204\345\275\225/I-\345\244\226\351\203\250\346\234\215\345\212\241\346\217\220\344\276\233\345\225\206/index.md" delete mode 100644 "docs/white-book/\351\231\204\345\275\225/index.md" diff --git a/docs/white-book/00-Manifesto/01-Vision.md b/docs/white-book/00-Manifesto/01-Vision.md new file mode 100644 index 000000000..f27ce7552 --- /dev/null +++ b/docs/white-book/00-Manifesto/01-Vision.md @@ -0,0 +1,25 @@ +# 01. 项目愿景 (Vision) + +## WebOS for Crypto + +KeyApp 不仅仅是一个多链钱包,它是一个 **去中心化 DApp 运行时 (Decentralized DApp Runtime)**,即 **WebOS**。 + +### 核心理念 + +1. **微内核 (Micro-Kernel)**: + * 通过 `miniapp-runtime` 实现应用进程隔离。 + * 每个 DApp 运行在独立的 iframe 沙箱中。 + * 通过 `BioBridge` 进行受控的跨进程通信。 + +2. **驱动分离 (Driver Abstraction)**: + * 底层链的差异被封装在 `ChainAdapter` 层。 + * 上层应用通过统一的 `ApiProvider` 接口与链交互。 + * 支持动态加载新的链驱动。 + +3. **沉浸式体验 (Immersive Shell)**: + * 提供类似原生 OS 的多任务管理 (Stack View)。 + * 支持手势导航、FLIP 动画和系统级通知。 + +### 为什么这样做? + +传统的 DApp 浏览器只是一个 WebView。KeyApp 试图在浏览器中重建操作系统的核心能力:**安全隔离**、**资源调度** 和 **统一交互**。这使得 KeyApp 能够成为连接 Web2 用户与 Web3 世界的终极网关。 diff --git a/docs/white-book/00-Manifesto/02-Tech-Stack.md b/docs/white-book/00-Manifesto/02-Tech-Stack.md new file mode 100644 index 000000000..a4b501f93 --- /dev/null +++ b/docs/white-book/00-Manifesto/02-Tech-Stack.md @@ -0,0 +1,30 @@ +# 02. 技术栈清单 (Tech Stack) + +KeyApp 选用了最现代化的前端技术栈,以支撑 WebOS 的复杂性。 + +## Core Framework +* **React 19**: 利用最新的 Hooks 和并发特性。 +* **Vite 6**: 极速构建工具,支持 ESM。 +* **TypeScript 5**: 严格类型安全。 + +## State Management +* **TanStack Store**: 轻量级、原子化的全局状态管理 (替代 Redux/Zustand)。 +* **TanStack Query (v5)**: 异步数据管理,用于链上数据缓存和同步。 + +## Navigation & Shell +* **Stackflow**: 仿原生 iOS/Android 的路由和页面栈管理。 + * *Why?* React Router 无法提供原生 App 的页面堆叠和手势体验。 + +## UI System +* **Tailwind CSS 4**: 原子化 CSS 引擎。 +* **shadcn/ui**: 基于 Radix UI 的可定制组件库。 +* **Framer Motion / Motion**: 负责复杂的 FLIP 动画和转场。 + +## Testing +* **Vitest**: 单元测试 (兼容 Jest API)。 +* **Playwright**: 端到端 (E2E) 测试。 + +## Architecture Patterns +* **Dependency Injection (DI)**: 通过 `context.tsx` 实现服务解耦。 +* **Adapter Pattern**: 用于多链适配 (`chain-adapter`)。 +* **Actor Model**: 用于 `miniapp-runtime` 的进程通信。 diff --git a/docs/white-book/00-Manifesto/03-Architecture.md b/docs/white-book/00-Manifesto/03-Architecture.md new file mode 100644 index 000000000..7184fef42 --- /dev/null +++ b/docs/white-book/00-Manifesto/03-Architecture.md @@ -0,0 +1,55 @@ +# 03. 宏观架构图 (Architecture Map) + +KeyApp 遵循典型的 **分层架构 (Layered Architecture)**,模仿操作系统的设计。 + +```mermaid +graph TD + subgraph "App Layer (DApps)" + DApp1[Uniswap] + DApp2[AAVE] + Miniapp[System Miniapps] + end + + subgraph "Shell Layer (System UI)" + Router[Stackflow Router] + TaskManager[Task View] + SystemApps[Wallet / Settings] + end + + subgraph "Kernel Layer (Miniapp Runtime)" + Process[Process Manager] + Window[Window Manager] + Sandbox[Iframe Sandbox] + Bridge[BioBridge IPC] + end + + subgraph "Service Layer (Business Logic)" + WalletCore[Wallet Core] + Store[State Store] + Identity[Biometric / Auth] + end + + subgraph "Driver Layer (Chain Adapters)" + HAL[ApiProvider Interface] + EVM[EVM Driver] + UTXO[BTC Driver] + TVM[Tron Driver] + end + + DApp1 --> Sandbox + Sandbox --> Bridge + Bridge --> ServiceLayer + SystemApps --> ServiceLayer + ServiceLayer --> HAL + HAL --> EVM & UTXO & TVM +``` + +## 目录映射 + +| 架构层级 | 对应代码目录 | 对应文档 | +| :--- | :--- | :--- | +| **Kernel** | `src/services/miniapp-runtime` | `01-Kernel-Ref` | +| **Shell** | `src/stackflow` | `03-Shell-Guide` | +| **Service** | `src/services/*`, `src/stores` | `10-Wallet-Guide` | +| **Driver** | `src/services/chain-adapter` | `02-Driver-Ref` | +| **UI** | `src/components`, `src/styles` | `03-UI-Ref` | diff --git a/docs/white-book/00-Manifesto/04-Get-Started.md b/docs/white-book/00-Manifesto/04-Get-Started.md new file mode 100644 index 000000000..20b2636b9 --- /dev/null +++ b/docs/white-book/00-Manifesto/04-Get-Started.md @@ -0,0 +1,36 @@ +# 04. 快速开始 (Get Started) + +## 环境要求 +* **Node.js**: v20+ +* **pnpm**: v9+ (推荐 v10) +* **Git**: v2.40+ + +## 启动流程 + +1. **安装依赖** + ```bash + pnpm install + ``` + +2. **启动开发服务器** + ```bash + pnpm dev + ``` + 这将启动 Vite 服务,默认访问地址为 `http://localhost:5173`。 + +3. **运行测试** + * 单元测试: `pnpm test` + * 类型检查: `pnpm typecheck` + +4. **构建** + ```bash + pnpm build + ``` + +## 常用命令别名 (Agent) + +我们封装了一套 `pnpm agent` 工具链,辅助 AI 开发: + +* `pnpm agent readme`: 获取项目索引和上下文。 +* `pnpm agent worktree create `: 创建隔离开发环境。 +* `pnpm agent claim `: 领取任务。 diff --git a/docs/white-book/00-Manifesto/05-Workflows.md b/docs/white-book/00-Manifesto/05-Workflows.md new file mode 100644 index 000000000..fb3caf6e0 --- /dev/null +++ b/docs/white-book/00-Manifesto/05-Workflows.md @@ -0,0 +1,24 @@ +# 05. 开发工作流 (Workflows) + +为了保证代码质量和 AI 协作的高效性,我们强制执行以下工作流。 + +## 1. Git Worktree 模式 +永远不要在主目录直接开发。必须为每个任务创建一个独立的 worktree: + +```bash +pnpm agent worktree create fix-bug-123 --branch fix/issue-123 --base main +cd .git-worktree/fix-bug-123 +``` + +这保证了环境的隔离性,避免了 `node_modules` 污染和分支切换的开销。 + +## 2. Spec Mode (规划优先) +在开始编码之前,AI 必须进入 **Spec Mode**,撰写详细的实现计划。只有用户批准 Spec 后,才能退出 Spec Mode 开始写代码。 + +## 3. Pull Request 规范 +* **Title**: 遵循 Conventional Commits (e.g., `feat(wallet): add multi-chain support`). +* **Body**: 清晰描述 Changes, Reason, 和 Test Plan。 +* **Review**: 所有 PR 必须经过 CI 检查并通过 Review 才能合并。 + +## 4. CD 流程 +* **gh-pages**: 我们使用 `gh-pages` 分支托管预览版。CD 流程会自动 force-push 构建产物到该分支。 diff --git a/docs/white-book/00-Manifesto/06-Guidelines.md b/docs/white-book/00-Manifesto/06-Guidelines.md new file mode 100644 index 000000000..8c2eeb50a --- /dev/null +++ b/docs/white-book/00-Manifesto/06-Guidelines.md @@ -0,0 +1,31 @@ +# 06. 编码规范 (Guidelines) + +> 源自原 `00-必读/best-practices.md`,持续更新。 + +## Do's ✅ + +### 架构与模式 +* **使用 Hooks 访问服务**: 永远不要直接导入 Service 单例,使用 `useWallet()`, `useBiometric()` 等 Hooks。 +* **Zod Schema 验证**: 所有外部输入(API 响应、URL 参数)必须经过 Zod 验证。 + ```typescript + const ResponseSchema = z.object({ result: z.string() }); + const data = ResponseSchema.parse(json); + ``` +* **Adapter 模式**: 涉及链交互时,必须通过 `ChainProvider`,禁止直接调用特定链的 API。 + +### UI 开发 +* **Tailwind CSS**: 优先使用 Tailwind 类名,避免手写 `style={{}}`。 +* **语义化颜色**: 使用 `text-muted-foreground` 而不是 `text-gray-500`。 +* **响应式**: 考虑移动端优先,使用 `@xs:`, `@md:` 断点。 + +## Don'ts ❌ + +* **禁止 hack**: 不要使用 `as any` 绕过类型检查。 +* **禁止硬编码**: 文本必须放入 `i18n`,API URL 必须放入配置文件。 +* **禁止过度封装**: 不要为了复用而创造极其复杂的组件,保持组件的单一职责。 + +## 最佳实践清单 + +* **滚动动画**: 使用 CSS `scroll-driven animations` 而不是 JS 监听 scroll 事件。 +* **组件样式**: 复杂组件使用 CSS Modules (`*.module.css`) 配合 Tailwind。 +* **测试**: 业务逻辑必须有 Unit Test,关键流程必须有 E2E Test。 diff --git a/docs/white-book/00-Manifesto/README.md b/docs/white-book/00-Manifesto/README.md new file mode 100644 index 000000000..ed67ec4db --- /dev/null +++ b/docs/white-book/00-Manifesto/README.md @@ -0,0 +1,18 @@ +# 🧭 Book E1: The Manifesto (开发者手册) + +> **KeyApp 开发者的第一站**。这里包含了项目的愿景、架构概览、技术栈清单以及核心的编码准则。 + +## 📖 目录 + +* [01-Vision.md](./01-Vision.md) - **WebOS for Crypto**:为什么 KeyApp 不仅仅是一个钱包? +* [02-Tech-Stack.md](./02-Tech-Stack.md) - 技术栈清单 (React 19, Vite, TanStack, Stackflow) +* [03-Architecture.md](./03-Architecture.md) - 宏观架构图 (Kernel - Shell - Driver - App) +* [04-Get-Started.md](./04-Get-Started.md) - 从零开始:环境搭建与启动 +* [05-Workflows.md](./05-Workflows.md) - AI 协作流 (Worktree, Spec Mode, PR) +* [06-Guidelines.md](./06-Guidelines.md) - 编码规范 (Hooks, i18n, Comments) + +## 🎯 核心原则 + +1. **AI-Native**: 所有代码和文档的首要读者是 AI Agent。保持上下文清晰、原子化。 +2. **WebOS 架构**: 我们在浏览器中构建操作系统。理解 Kernel (运行时)、Driver (链适配) 和 Shell (界面) 的分离。 +3. **Spec First**: 先思考 (Spec Mode),再行动。所有复杂变更必须先产出 Spec。 diff --git "a/docs/white-book/00-\345\277\205\350\257\273/best-practices.md" "b/docs/white-book/00-\345\277\205\350\257\273/best-practices.md" deleted file mode 100644 index 391a3f5cb..000000000 --- "a/docs/white-book/00-\345\277\205\350\257\273/best-practices.md" +++ /dev/null @@ -1,33 +0,0 @@ -# 最佳实践 - -> 由 `pnpm agent practice` 维护。 - -## 列表 - -- ❌ Radix Dialog / position:fixed → ✅ Stackflow BottomSheet/Modal -- ❌ React Router → ✅ Stackflow push/pop/replace -- ❌ 复制 mpay 代码 → ✅ 理解后用 React/TS 重写 -- ❌ 随意创建 store → ✅ 遵循 stores/ 现有模式 -- ❌ 明文选择器 → ✅ data-testid -- ❌ 安装新 UI 库 → ✅ shadcn/ui(已集成) -- ❌ 新建 CSS → ✅ Tailwind CSS -- ❌ text-secondary → ✅ text-muted-foreground 或 bg-secondary text-secondary-foreground(详见白皮书 02-设计篇/02-视觉设计/theme-colors) -- ❌ bg-primary text-white → ✅ bg-primary text-primary-foreground(暗色模式下 text-white 对比度不足) -- ❌ bg-muted 无文字色 → ✅ bg-muted text-muted-foreground(详见白皮书 02-设计篇/02-视觉设计/dark-mode) -- ❌ bg-gray-100 无 dark: 变体 → ✅ bg-gray-100 dark:bg-gray-800 或使用 bg-muted -- ❌ getByText('硬编码中文') → ✅ getByText(t('i18n.key')) 或 getByTestId -- ❌ password/Password(宽泛含义) → ✅ walletLock(钱包锁)/ twoStepSecret(安全密码)/ payPassword(支付密码)等具体命名 -- 圆形元素必须使用 aspect-square 标记,与 w-*/h-*/size-* 不冲突,是规范要求 -- 组件尺寸属性要考虑响应式布局,如 lg 尺寸应包含 @xs:w-12 等容器查询断点 -- ❌ JS scroll 事件监听 → ✅ CSS scroll-driven animations(animation-timeline: scroll()) -- 滚动驱动动画使用 `scroll-timeline: --name block` 定义 + `animation-timeline: --name` 绑定 -- 跨层级绑定使用 `timeline-scope: --name` 在公共祖先上声明 -- 紧凑头部效果使用 `animation-range: 0 80px` 限制动画范围 -- ⚠️ scroll-driven animations 是渐进增强:初始状态必须是可用的(如 opacity-0),不支持时保持初始状态 -- E2E 截图变更后运行 `pnpm e2e:audit` 检查残留截图,详见白皮书 08-测试篇/03-Playwright配置/e2e-best-practices -- 组件专属样式使用 CSS Modules:`component-name.module.css` + `import styles from './xxx.module.css'` -- CSS Modules 适用场景:@keyframes 动画、伪元素(::before/::after)、复杂选择器(:focus-within)、scroll-driven animations -- CSS Modules 与 Tailwind 混用:`className={cn(styles.header, 'sticky top-0 z-10 px-5')}` -- 优先级:CSS Modules > globals.css,组件样式应内聚到组件目录 -- ❌ as TypeAssertion → ✅ z.looseObject().safeParse() 验证外部 API 响应 -- ❌ z.record(z.record(...)) → ✅ z.record(z.string(), z.record(z.string(), schema))(Zod 4 嵌套 record 需显式 key 类型) diff --git "a/docs/white-book/00-\345\277\205\350\257\273/index.md" "b/docs/white-book/00-\345\277\205\350\257\273/index.md" deleted file mode 100644 index 90c8021b2..000000000 --- "a/docs/white-book/00-\345\277\205\350\257\273/index.md" +++ /dev/null @@ -1,145 +0,0 @@ -# AI 开发指南 - 必读 - -> **重要**:本章节是 AI 模型开发本项目前的必读内容。 - ---- - -## 序言:纠正 AI 直觉 - -AI 模型在开发移动端 Web 应用时,往往会基于通用 Web 开发经验做出直觉性的选择。但本项目使用了特定的技术栈和架构模式,这些"直觉"可能导致严重的问题。 - -### 🚫 常见错误直觉 - -最佳实践已独立维护在: - -- `00-必读/best-practices.md` -- `pnpm agent practice list` - -### 📚 为什么会出错? - -1. **Stackflow 是页面栈管理器**:它管理整个页面栈的动画和生命周期。使用 `position: fixed` 会绕过 Stackflow 的控制,导致动画冲突、页面"抖动"等问题。 - -2. **本项目是移动端优先**:需要原生 App 般的用户体验,包括滑动返回、栈式导航等。传统 Web 的弹窗模式不适用。 - -3. **组件已经封装好**:不要重新发明轮子,查看 `src/components/` 和 `src/stackflow/activities/sheets/` 下的现有实现。 - ---- - -## 目录索引 - -使用以下命令获取白皮书完整目录: - -```bash -# 列出所有白皮书章节 -find docs/white-book -name "*.md" | sort -``` - -或者让 AI 执行 Grep 命令动态获取。 - -### 核心章节速查 - -| 章节 | 路径 | 何时阅读 | -|------|------|---------| -| **导航系统** | `03-架构篇/03-导航系统/` | 实现页面跳转、Tab 切换时 | -| **Sheet 组件** | `05-组件篇/01-基础组件/Sheet.md` | 实现底部弹出层时 | -| **Dialog 组件** | `05-组件篇/01-基础组件/Dialog.md` | 实现居中对话框时 | -| **服务架构** | `04-服务篇/01-服务架构/` | 调用后端 API 或链服务时 | -| **钱包存储** | `04-服务篇/08-钱包数据存储/` | 操作钱包数据时 | -| **国际化** | `07-国际化篇/` | 添加文案或多语言时 | -| **测试策略** | `08-测试篇/` | 编写测试用例时 | -| **E2E 最佳实践** | `08-测试篇/03-Playwright配置/e2e-best-practices.md` | **必读:禁止使用明文选择器** | - ---- - -## 外部文档链接 - -以下是本项目依赖的核心库的 LLM 友好文档: - -| 库 | 文档链接 | 用途 | -|---|---------|------| -| **Stackflow** | https://stackflow.so/llms-full.txt | 页面栈导航、Activity、Sheet、Modal | -| **shadcn/ui** | https://ui.shadcn.com/docs | UI 组件库 | -| **TanStack Query** | https://tanstack.com/query/latest | 数据获取和缓存 | -| **TanStack Store** | https://tanstack.com/store/latest | 状态管理 | - ---- - -## 开发检查清单 - -在开始编码前,请确认: - -- [ ] 是否已阅读相关白皮书章节? -- [ ] 是否已查看 `src/` 下的类似实现? -- [ ] 弹出层是否使用了 Stackflow 的 `BottomSheet` 或 `Modal`? -- [ ] 是否遵循了现有的代码风格和模式? -- [ ] 是否添加了必要的国际化文案? -- [ ] 是否更新了相关测试? - ---- - -## 快速参考 - -### Stackflow Activity 类型 - -```tsx -// 普通全屏页面 -import { AppScreen } from "@stackflow/plugin-basic-ui"; - -// 底部弹出层(Sheet) -import { BottomSheet } from "@stackflow/plugin-basic-ui"; - -// 居中对话框(Modal) -import { Modal } from "@stackflow/plugin-basic-ui"; -``` - -### 导航操作 - -```tsx -import { useFlow } from "@/stackflow"; - -const { push, pop, replace } = useFlow(); - -// 打开新页面 -push("ActivityName", { param: "value" }); - -// 返回上一页 -pop(); - -// 替换当前页面 -replace("ActivityName", { param: "value" }); -``` - -### Sheet Activity 模式 - -当需要创建新的 Sheet 时,参考 `src/stackflow/activities/sheets/` 下的实现: - -1. 创建 Activity 组件,使用 `` 或 `` 包裹 -2. 在 `stackflow.ts` 中注册路由和组件 -3. 在 `sheets/index.ts` 中导出 - ---- - -## 常见问题 - -### Q: 为什么我的弹窗导致页面"抖动"或消失? - -A: 你可能使用了 Radix Dialog 或自定义 `position: fixed`。这会与 Stackflow 的动画系统冲突。请改用 Stackflow 的 `BottomSheet` 或 `Modal`。 - -### Q: 如何传递数据给 Sheet 并获取返回值? - -A: 使用全局回调模式。参考 `WalletLockConfirmJob` 的实现: - -```tsx -// 设置回调 -setWalletLockConfirmCallback((patternKey) => { - // 处理返回的图案密钥 -}); - -// 打开 Sheet -push("WalletLockConfirmJob", {}); -``` - -### Q: 什么时候用 BottomSheet,什么时候用 Modal? - -- **BottomSheet**:选择列表、表单输入、确认操作(从底部滑入) -- **Modal**:警告、确认对话框(居中显示) diff --git a/docs/white-book/01-Kernel-Ref/00-Overview.md b/docs/white-book/01-Kernel-Ref/00-Overview.md new file mode 100644 index 000000000..271c5e0df --- /dev/null +++ b/docs/white-book/01-Kernel-Ref/00-Overview.md @@ -0,0 +1,38 @@ +# 00. 内核架构全景 (Kernel Overview) + +KeyApp 的内核 (`miniapp-runtime`) 是一个运行在浏览器主线程中的 **微内核 (Micro-Kernel)**。它的主要职责不是处理业务逻辑,而是管理 **进程 (Process)** 和 **资源 (Resource)**。 + +## 核心组件 + +1. **Process Manager**: 维护所有 DApp 的生命周期 (`MiniappInstance`)。 +2. **Window Manager**: 管理所有可见窗口 (`MiniappPresentation`) 及其 Z-Order 和动画。 +3. **Iframe Sandbox**: 为每个 DApp 创建隔离的执行环境。 +4. **BioBridge**: 提供宿主 (Host) 与微应用 (Miniapp) 之间的安全通信通道。 + +## 架构图 + +```mermaid +graph TD + subgraph "Host (KeyApp Main Thread)" + Kernel[Miniapp Kernel] + Store[Runtime Store] + BridgeHost[BioBridge Host] + + Kernel --> Store + Kernel --> BridgeHost + end + + subgraph "Sandbox (Iframe)" + BridgeGuest[BioBridge Guest] + DAppCode[DApp Code] + + DAppCode --> BridgeGuest + end + + BridgeHost <-->|postMessage| BridgeGuest +``` + +## 设计哲学 + +* **UI as a Function of State**: 内核本身是一个基于 TanStack Store 的状态机。所有的 UI 变化(如窗口打开、关闭)都是状态变更的副作用。 +* **Isolation by Default**: 默认情况下,DApp 无法访问任何系统资源,必须通过 Bridge 请求权限。 diff --git a/docs/white-book/01-Kernel-Ref/01-Process/01-State-Machine.md b/docs/white-book/01-Kernel-Ref/01-Process/01-State-Machine.md new file mode 100644 index 000000000..39a5fa203 --- /dev/null +++ b/docs/white-book/01-Kernel-Ref/01-Process/01-State-Machine.md @@ -0,0 +1,42 @@ +# 01. 进程状态机 (Process State Machine) + +Code: `src/services/miniapp-runtime/types.ts` + +KeyApp 中的每个微应用都有一个严格的状态机。理解这个状态机对于开发 Runtime 至关重要。 + +## 状态定义 (`MiniappState`) + +| 状态 | 描述 | 触发条件 | +| :--- | :--- | :--- | +| **preparing** | 准备中 | 用户点击图标,内核开始检查资源 (Icon, Slot, Iframe) | +| **launching** | 启动中 | 资源就绪,开始创建 iframe 并加载 URL | +| **splash** | 闪屏页 | 应用配置了 splash screen,且正在加载中 | +| **active** | 前台激活 | 应用加载完成,且处于前台,可交互 | +| **background** | 后台运行 | 用户切换到其他应用,iframe 被隐藏但未销毁 | +| **closing** | 关闭中 | 用户关闭应用,正在执行退出动画 | + +## 状态流转图 + +```mermaid +stateDiagram-v2 + [*] --> preparing + preparing --> launching: Resources Ready + launching --> splash: Has Splash Config + launching --> active: No Splash & Loaded + splash --> active: Timeout or Dismissed + active --> background: Switch App + background --> active: Switch Back + active --> closing: Close App + background --> closing: Close App + closing --> [*]: Cleanup +``` + +## Flow vs State + +除了静态的 `state`,我们还有一个 `flow` 属性,用于描述 **动画方向**: +* `opening` / `opened` +* `backgrounding` / `backgrounded` +* `foregrounding` +* `closing` + +UI 层通过监听 `flow` 来决定播放哪种 FLIP 动画。 diff --git a/docs/white-book/01-Kernel-Ref/01-Process/02-Scheduling.md b/docs/white-book/01-Kernel-Ref/01-Process/02-Scheduling.md new file mode 100644 index 000000000..fa2baf7d9 --- /dev/null +++ b/docs/white-book/01-Kernel-Ref/01-Process/02-Scheduling.md @@ -0,0 +1,22 @@ +# 02. 调度与保活 (Scheduling) + +Code: `src/services/miniapp-runtime/iframe-manager.ts` + +为了防止浏览器内存溢出,KeyApp 实现了一套简单的调度策略来管理后台应用。 + +## 后台限制 (Background Limit) + +* **最大后台数**: 默认为 **4** 个 (`MAX_BACKGROUND_IFRAMES`)。 +* **淘汰策略**: **LRU (Least Recently Used)**。当后台应用数量超过限制时,最久未使用的应用会被强行销毁。 + +## Watchdog 机制 + +为了防止应用在关闭过程中卡死(例如动画回调未触发),内核启动了一个 **Watchdog Timer**。 +* **超时时间**: 30秒 (`DISMISS_WATCHDOG_DELAY`)。 +* **行为**: 如果在超时后应用仍未完成关闭流程,Watchdog 会强制执行 `finalizeCloseApp` 进行资源清理。 + +## 准备阶段超时 + +在 `preparing` 阶段,内核会等待 DOM 元素(图标、插槽)就绪。为了防止无限等待: +* **超时时间**: 2.5秒 (`PREPARING_TIMEOUT`)。 +* **行为**: 超时后自动取消启动,并提示用户错误信息。 diff --git a/docs/white-book/01-Kernel-Ref/02-Window/01-Presentation-Model.md b/docs/white-book/01-Kernel-Ref/02-Window/01-Presentation-Model.md new file mode 100644 index 000000000..829b06f9b --- /dev/null +++ b/docs/white-book/01-Kernel-Ref/02-Window/01-Presentation-Model.md @@ -0,0 +1,25 @@ +# 01. 窗口模型 (Presentation Model) + +Code: `src/services/miniapp-runtime/runtime-refs.ts` + +KeyApp 支持多窗口管理,每个窗口由一个 `MiniappPresentation` 对象描述。 + +## 核心概念 + +### 1. Presentation +描述了一个应用在屏幕上的呈现状态: +* `state`: `hidden` | `presenting` | `presented` | `dismissing` +* `desktop`: 目标桌面 (`stack` | `grid`)。大多数 DApp 运行在 `stack` 桌面。 +* `zOrder`: Z 轴层级。激活的应用会自动获得最高的 `zOrder`。 + +### 2. Runtime Refs +为了实现精细的动画控制,内核维护了一组 DOM 引用 (`runtime-refs`): +* `IconRef`: 桌面图标的 DOM 元素。 +* `SlotRef`: 应用窗口的目标容器位置。 +* `IframeRef`: 实际的内容载体。 + +## 窗口层级管理 (Z-Order) + +内核维护一个全局递增的 `zOrderSeed`。 +* 每当应用被激活 (`activateApp`) 或请求聚焦 (`requestFocus`),它会被分配 `zOrderSeed + 1`。 +* 这确保了当前操作的应用总是覆盖在其他应用之上。 diff --git a/docs/white-book/01-Kernel-Ref/02-Window/02-FLIP-Animation.md b/docs/white-book/01-Kernel-Ref/02-Window/02-FLIP-Animation.md new file mode 100644 index 000000000..b65ca1cca --- /dev/null +++ b/docs/white-book/01-Kernel-Ref/02-Window/02-FLIP-Animation.md @@ -0,0 +1,20 @@ +# 02. FLIP 动画引擎 (FLIP Animation) + +KeyApp 的标志性特征是其流畅的 **App 打开/关闭动画**,这完全基于 **FLIP (First, Last, Invert, Play)** 技术实现。 + +## 原理 + +1. **First (初始态)**: 获取图标 (`IconRef`) 在屏幕上的 `DOMRect`。 +2. **Last (最终态)**: 获取应用窗口 (`SlotRef`) 在屏幕上的 `DOMRect`。 +3. **Invert (反转)**: + * 计算两个 Rect 之间的位移 (`deltaX`, `deltaY`) 和缩放比例 (`scaleX`, `scaleY`)。 + * 将窗口元素通过 CSS `transform` 瞬间移动到图标的位置和大小,使其看起来像是在图标那里。 +4. **Play (播放)**: + * 移除 `transform`,并添加 `transition` 属性。 + * 浏览器会自动补间,使窗口从图标位置平滑放大到全屏位置。 + +## 性能优化 + +* **Will-Change**: 在动画开始前添加 `will-change: transform` 提示浏览器创建合成层。 +* **Layout ID**: 利用 Framer Motion 的 `layoutId` 自动处理复杂的共享元素过渡。 +* **遮罩同步**: 动画过程中,同步调整圆角 (`borderRadius`) 和遮罩,确保视觉连贯性。 diff --git a/docs/white-book/01-Kernel-Ref/03-Sandbox/01-Iframe-Manager.md b/docs/white-book/01-Kernel-Ref/03-Sandbox/01-Iframe-Manager.md new file mode 100644 index 000000000..68f2ab53b --- /dev/null +++ b/docs/white-book/01-Kernel-Ref/03-Sandbox/01-Iframe-Manager.md @@ -0,0 +1,25 @@ +# 01. Iframe 管理 (Iframe Manager) + +Code: `src/services/miniapp-runtime/iframe-manager.ts` + +Iframe 是 DApp 的容器。KeyApp 对 Iframe 进行了深度的定制管理。 + +## 创建策略 + +* **URL 构造**: 基础 URL + 上下文参数 (Context Params)。 +* **Sandbox**: 启用严格的沙箱模式: + ```typescript + iframe.sandbox.add('allow-scripts', 'allow-forms', 'allow-same-origin') + ``` + 注意:通常不开启 `allow-top-navigation`,防止 DApp 篡改主页面。 + +## 挂载策略 + +为了支持后台保活,KeyApp 使用了 **双容器策略**: + +1. **Visible Container**: 用于存放前台 Active 应用的 iframe。 +2. **Hidden Container**: 用于存放后台 Background 应用的 iframe。 + * 样式: `position: fixed; top: -9999px; visibility: hidden`。 + * 目的: 保持 DOM 节点存在,不触发 reload,但移出可视区域。 + +当应用在 前台/后台 切换时,我们只是简单地将 iframe 节点在两个容器之间 `appendChild` (移动节点),而不是销毁重建。这保证了 DApp 状态不丢失。 diff --git a/docs/white-book/01-Kernel-Ref/03-Sandbox/02-BioBridge-Protocol.md b/docs/white-book/01-Kernel-Ref/03-Sandbox/02-BioBridge-Protocol.md new file mode 100644 index 000000000..b9990a533 --- /dev/null +++ b/docs/white-book/01-Kernel-Ref/03-Sandbox/02-BioBridge-Protocol.md @@ -0,0 +1,40 @@ +# 02. BioBridge 协议 (BioBridge Protocol) + +**BioBridge** 是 Host 和 Guest (DApp) 之间的通信桥梁。 + +## 通信机制 + +基于 `window.postMessage` 实现双向 RPC 调用。 + +### 1. 请求 (Request) +DApp -> Host: +```json +{ + "id": 123, + "method": "bio.wallet.requestAccounts", + "params": [] +} +``` + +### 2. 响应 (Response) +Host -> DApp: +```json +{ + "id": 123, + "result": ["0x..."], + "error": null +} +``` + +## API 命名空间 + +* `bio.wallet.*`: 钱包相关 (连接、签名、转账) +* `bio.device.*`: 设备能力 (剪贴板、震动、生物识别) +* `bio.window.*`: 窗口控制 (关闭、全屏) + +## 安全检查 + +在处理任何请求之前,内核会检查: +1. **Origin**: 消息来源是否匹配 Manifest 中的定义。 +2. **Permission**: DApp 是否申请了对应的权限 (e.g., `wallet:write`). +3. **User Consent**: 敏感操作 (签名、授权) 必须弹出系统级对话框供用户确认。 diff --git a/docs/white-book/01-Kernel-Ref/README.md b/docs/white-book/01-Kernel-Ref/README.md new file mode 100644 index 000000000..0bf7bf339 --- /dev/null +++ b/docs/white-book/01-Kernel-Ref/README.md @@ -0,0 +1,18 @@ +# 📘 Book T1: The Kernel Reference (内核技术参考) + +> **Core of the WebOS** +> +> 本书深入解析 `miniapp-runtime` 的内部机制,解释 KeyApp 如何在浏览器中实现进程隔离、窗口管理和跨进程通信。 + +## 📖 目录 + +* [00-Overview.md](./00-Overview.md) - 内核架构全景 +* **01-Process (进程管理)** + * [01-State-Machine.md](./01-Process/01-State-Machine.md) - 进程状态机 + * [02-Scheduling.md](./01-Process/02-Scheduling.md) - 调度与保活 +* **02-Window (窗口管理)** + * [01-Presentation-Model.md](./02-Window/01-Presentation-Model.md) - 窗口模型 + * [02-FLIP-Animation.md](./02-Window/02-FLIP-Animation.md) - 动画引擎 +* **03-Sandbox (沙箱隔离)** + * [01-Iframe-Manager.md](./03-Sandbox/01-Iframe-Manager.md) - Iframe 管理 + * [02-BioBridge-Protocol.md](./03-Sandbox/02-BioBridge-Protocol.md) - BioBridge 协议 diff --git "a/docs/white-book/01-\344\272\247\345\223\201\347\257\207/01-\344\272\247\345\223\201\346\204\277\346\231\257/index.md" "b/docs/white-book/01-\344\272\247\345\223\201\347\257\207/01-\344\272\247\345\223\201\346\204\277\346\231\257/index.md" deleted file mode 100644 index d8a2b4beb..000000000 --- "a/docs/white-book/01-\344\272\247\345\223\201\347\257\207/01-\344\272\247\345\223\201\346\204\277\346\231\257/index.md" +++ /dev/null @@ -1,164 +0,0 @@ -# 第一章:产品愿景 - -> **让每个人都能安全、简单地管理自己的数字资产。** - ---- - -## 1.1 产品定位 - -BFM Pay 是一款**多链数字钱包移动应用**,目标是成为用户进入 Web3 世界的可信赖入口。 - -### 核心理念 - -我们相信:**复杂的区块链技术不应成为普通用户的障碍。** - -| 传统钱包的问题 | BFM Pay 的解决方案 | -|--------------|-------------------| -| 需要理解"助记词""Gas费"等概念 | 用通俗语言解释,引导式操作 | -| 多个钱包管理不同链的资产 | 一个钱包,多链支持 | -| 界面复杂,功能堆砌 | 渐进式复杂度,新手友好 | -| 转错地址无法挽回 | 多重确认,地址簿管理 | - -### 产品形态 - -``` -┌─────────────────────────────────────────┐ -│ BFM Pay │ -├─────────────────────────────────────────┤ -│ │ -│ ┌─────────┐ ┌─────────┐ │ -│ │ Web │ │ DWEB │ │ -│ │ 版本 │ │ 版本 │ │ -│ └────┬────┘ └────┬────┘ │ -│ │ │ │ -│ └────────┬────────┘ │ -│ │ │ -│ ┌───────▼───────┐ │ -│ │ 统一核心 │ │ -│ │ React + TS │ │ -│ └───────────────┘ │ -│ │ -└─────────────────────────────────────────┘ -``` - -- **Web 版本**:浏览器访问,无需安装 -- **DWEB 版本**:Plaoc 原生应用,更深度的系统集成 - ---- - -## 1.2 目标市场 - -### 主要市场 - -**BioforestChain 生态用户** -- BFMeta、PMChain、CCChain 等链的持有者 -- 需要与 DWEB 应用交互的用户 -- 参与生态质押和投票的用户 - -### 拓展市场 - -**主流公链用户** -- Ethereum、BSC、Tron 等链的持有者 -- 寻找多链管理方案的用户 -- 对安全性有较高要求的用户 - ---- - -## 1.3 价值主张 - -### 对用户的价值 - -``` -┌─────────────────────────────────────────────────┐ -│ │ -│ 🔐 安全 → 私钥本地存储,永不上传 │ -│ │ -│ 🌐 多链 → 一个钱包,管理所有链资产 │ -│ │ -│ 📱 便捷 → 随时随地,轻松转账收款 │ -│ │ -│ 🔗 生态 → 无缝连接 DWEB 应用 │ -│ │ -└─────────────────────────────────────────────────┘ -``` - -### 核心功能价值 - -| 功能 | 用户价值 | 技术实现 | -|-----|---------|---------| -| 创建钱包 | 30秒拥有自己的数字身份 | 助记词生成 + 多链派生 | -| 资产查看 | 一目了然的资产状况 | 多链余额聚合 | -| 转账收款 | 像发微信一样简单 | 统一的转账流程 | -| 跨链质押 | 参与生态建设,获取收益 | Mint/Burn 机制 | -| DWEB 授权 | 安全地与应用交互 | Plaoc 协议 | - ---- - -## 1.4 与 mpay 的关系 - -BFM Pay 是 mpay 的**技术重构版本**,而非简单的 UI 改版。 - -### 保留 - -- ✅ 核心业务逻辑 -- ✅ 多链支持能力 -- ✅ DWEB 协议兼容 -- ✅ 用户数据格式 - -### 提升 - -| 方面 | mpay (Angular) | BFM Pay (React) | -|-----|---------------|-----------------| -| 框架 | Angular 15 | React 19 | -| 状态管理 | Service + RxJS | TanStack Store/Query | -| 导航 | Angular Router | Stackflow | -| 组件库 | Ionic | shadcn/ui | -| 测试 | Jasmine | Vitest + Playwright | -| 文档 | 无 | Storybook | - -### 原则 - -``` -参考,而非复制 -``` - -- 理解 mpay 的业务逻辑,不是复制代码 -- 质疑原始实现,发现并修复问题 -- 使用现代化技术栈重新实现 - ---- - -## 1.5 成功指标 - -### 用户体验指标 - -| 指标 | 目标 | -|-----|------| -| 首次创建钱包时间 | < 60秒 | -| 完成一笔转账时间 | < 30秒 | -| 新用户次日留存率 | > 40% | -| 用户满意度 (NPS) | > 50 | - -### 技术质量指标 - -| 指标 | 目标 | -|-----|------| -| 单元测试覆盖率 | > 70% | -| E2E 测试通过率 | 100% | -| Lighthouse 性能分 | > 90 | -| 首屏加载时间 | < 2秒 | - ---- - -## 本章小结 - -- BFM Pay 的愿景是让数字资产管理变得安全、简单 -- 核心定位是多链钱包,服务 BioforestChain 生态和主流公链用户 -- 是 mpay 的技术重构,保留业务逻辑,提升技术栈和用户体验 -- 成功需要同时满足用户体验和技术质量指标 - ---- - -## 下一章 - -继续阅读 [第二章:用户画像](../02-用户画像/),深入了解我们的目标用户。 diff --git "a/docs/white-book/01-\344\272\247\345\223\201\347\257\207/02-\347\224\250\346\210\267\347\224\273\345\203\217/index.md" "b/docs/white-book/01-\344\272\247\345\223\201\347\257\207/02-\347\224\250\346\210\267\347\224\273\345\203\217/index.md" deleted file mode 100644 index 56bf4345c..000000000 --- "a/docs/white-book/01-\344\272\247\345\223\201\347\257\207/02-\347\224\250\346\210\267\347\224\273\345\203\217/index.md" +++ /dev/null @@ -1,260 +0,0 @@ -# 第二章:用户画像 - -> 深入理解我们的用户是谁,他们需要什么 - ---- - -## 2.1 主要用户群体 - -### 👤 新手小白 (Alice) - -**基本信息** -- 年龄:28岁 -- 职业:设计师 -- 技术水平:普通用户,熟悉手机 App - -**背景故事** -> Alice 听朋友说起加密货币,想尝试但觉得技术门槛高。她用过支付宝和微信支付,但从未接触过区块链。 - -**痛点** -- 😰 害怕操作失误丢失资产 -- 😕 不理解"助记词""Gas 费"等概念 -- 🤔 担心被骗或黑客攻击 - -**期望** -- ✅ 像支付宝一样简单 -- ✅ 有清晰的操作引导 -- ✅ 出问题能找到帮助 - -**心智模型** -``` -钱包 = 银行账户 -助记词 = 银行卡密码 -转账 = 微信转账 -``` - ---- - -### 👤 进阶用户 (Bob) - -**基本信息** -- 年龄:35岁 -- 职业:程序员 -- 技术水平:熟悉区块链,使用过多款钱包 - -**背景故事** -> Bob 已有多个链的资产,使用过 MetaMask、Trust Wallet、imToken 等钱包。他对技术有一定了解,但讨厌在多个钱包间切换。 - -**痛点** -- 😤 需要在多个钱包间切换 -- 😫 跨链操作繁琐 -- 🙄 重复的设置和备份流程 - -**期望** -- ✅ 一个钱包管理所有链 -- ✅ 快速完成日常操作 -- ✅ 高级功能不被隐藏 - -**心智模型** -``` -钱包 = 资产管理工具 -效率 = 最重要的事 -安全 = 基本要求 -``` - ---- - -### 👤 活跃交易者 (Carol) - -**基本信息** -- 年龄:30岁 -- 职业:交易员 -- 技术水平:精通区块链,高频交易 - -**背景故事** -> Carol 每天进行多笔转账和交易,需要快速确认交易状态。她最担心的是转错地址或被钓鱼攻击。 - -**痛点** -- 😱 担心转错地址 -- ⏱️ 需要快速确认交易状态 -- 🎣 警惕钓鱼和欺诈 - -**期望** -- ✅ 地址簿管理常用地址 -- ✅ 实时交易状态更新 -- ✅ 完整的历史记录 - -**心智模型** -``` -钱包 = 交易终端 -安全 = 生命线 -速度 = 金钱 -``` - ---- - -## 2.2 用户心智模型 - -### 关键认知映射 - -| 用户认知 | 技术概念 | 产品设计对应 | -|---------|---------|-------------| -| "钱包 = 存钱的地方" | 密钥管理 | 首页突出显示余额,像银行 App | -| "地址 = 账号" | 公钥哈希 | 简化地址展示,提供复制和二维码 | -| "助记词 = 最高密码" | BIP39 种子 | 强调备份重要性,通俗类比解释 | -| "转账要确认" | 签名广播 | 双重确认机制,显示手续费 | -| "不同链 = 不同银行" | 多链架构 | 清晰的链切换,统一的操作体验 | - -### 心智模型图 - -``` -用户视角 技术视角 -───────────── ───────────── - 钱包 ←────────→ 密钥对 - │ │ - ▼ ▼ - 账户/地址 ←────────→ 公钥地址 - │ │ - ▼ ▼ - 余额 ←────────→ 链上状态 - │ │ - ▼ ▼ - 转账 ←────────→ 签名交易 -``` - ---- - -## 2.3 用户旅程阶段 - -### 阶段一:认知 - -> "这是什么?对我有什么用?" - -**用户状态** -- 第一次听说或看到 BFM Pay -- 可能来自朋友推荐或应用商店 - -**设计重点** -- 清晰的价值主张 -- 简洁的首屏信息 -- 降低认知负担 - ---- - -### 阶段二:入门 - -> "我要怎么开始?" - -**用户状态** -- 决定尝试,开始创建或导入钱包 -- 最容易流失的阶段 - -**设计重点** -- 引导式流程 -- 即时反馈 -- 允许跳过非必要步骤 - ---- - -### 阶段三:日常使用 - -> "我需要完成一个任务" - -**用户状态** -- 已有钱包,进行日常操作 -- 转账、收款、查看余额 - -**设计重点** -- 高效的操作路径 -- 减少点击次数 -- 记住用户偏好 - ---- - -### 阶段四:进阶探索 - -> "还有什么高级功能?" - -**用户状态** -- 熟悉基础功能 -- 探索质押、DApp 连接等 - -**设计重点** -- 功能可发现性 -- 不干扰基础体验 -- 渐进式披露 - ---- - -### 阶段五:问题解决 - -> "出问题了,怎么办?" - -**用户状态** -- 遇到错误或困惑 -- 寻求帮助 - -**设计重点** -- 清晰的错误信息 -- 可访问的帮助入口 -- 自助解决方案 - ---- - -## 2.4 用户优先级 - -### 功能优先级矩阵 - -``` - 高频 - │ - ┌────────────┼────────────┐ - │ 转账 │ 查看余额 │ - │ 收款 │ 切换链 │ - 高 │────────────┼────────────│ 低 - 价值 │ 备份 │ 设置 │ 价值 - │ DApp授权 │ 历史记录 │ - │────────────┼────────────│ - │ 质押 │ 地址簿 │ - │ 导入钱包 │ 网络设置 │ - └────────────┼────────────┘ - │ - 低频 -``` - -### 开发优先级 - -| 优先级 | 功能 | 目标用户 | -|-------|------|---------| -| P0 | 创建/导入钱包、查看余额、转账、收款 | 所有用户 | -| P1 | 多链切换、交易历史、备份助记词 | 所有用户 | -| P2 | 地址簿、应用锁、指纹支付 | 进阶用户 | -| P3 | 质押、DApp 授权、自定义网络 | 活跃用户 | - ---- - -## 2.5 竞品对比中的用户需求 - -### 用户痛点对比 - -| 痛点 | MetaMask | Trust Wallet | BFM Pay 方案 | -|-----|----------|--------------|-------------| -| 助记词理解困难 | 无解释 | 简单提示 | 通俗类比 + 引导 | -| 多链管理混乱 | 手动添加网络 | 预置网络 | 统一的链切换面板 | -| 转账地址输入 | 纯手动 | 地址簿 | 扫码 + 地址簿 + 历史 | -| Gas 费理解 | 技术术语 | 简化显示 | "网络费用" + 预估法币 | - ---- - -## 本章小结 - -- 三类核心用户:新手小白、进阶用户、活跃交易者 -- 用户心智模型决定产品设计语言 -- 用户旅程分五个阶段,每阶段有不同设计重点 -- 功能开发按用户价值和使用频率确定优先级 - ---- - -## 下一章 - -继续阅读 [第三章:用户故事](../03-用户故事/),了解具体的功能需求和验收标准。 diff --git "a/docs/white-book/01-\344\272\247\345\223\201\347\257\207/03-\347\224\250\346\210\267\346\225\205\344\272\213/index.md" "b/docs/white-book/01-\344\272\247\345\223\201\347\257\207/03-\347\224\250\346\210\267\346\225\205\344\272\213/index.md" deleted file mode 100644 index 7c546a4c3..000000000 --- "a/docs/white-book/01-\344\272\247\345\223\201\347\257\207/03-\347\224\250\346\210\267\346\225\205\344\272\213/index.md" +++ /dev/null @@ -1,456 +0,0 @@ -# 第三章:用户故事 - -> 定义完整的功能需求和验收标准 - ---- - -## 3.1 Epic 概览 - -BFM Pay 的功能需求组织为 10 个 Epic: - -| Epic | 名称 | 核心用户故事 | -|------|------|-------------| -| 1 | [拥有钱包](#epic-1-我想拥有一个自己的钱包) | 创建、导入钱包 | -| 2 | [查看资产](#epic-2-我想查看我的资产) | 余额查看、链切换 | -| 3 | [转账](#epic-3-我想给别人转账) | 发起转账、扫码 | -| 4 | [收款](#epic-4-我想收款) | 展示收款码 | -| 5 | [质押](#epic-5-我想进行跨链质押) | Mint/Burn | -| 6 | [安全设置](#epic-6-我想保护我的钱包安全) | 应用锁、备份 | -| 7 | [地址管理](#epic-7-我想管理常用地址) | 地址簿 | -| 8 | [DWEB 交互](#epic-8-我想与-dapp-交互) | 授权、签名 | -| 9 | [个性化](#epic-9-我想个性化我的钱包) | 语言、货币 | -| 10 | [我的](#epic-10-我的页面) | 设置入口 | - ---- - -## Epic 1: 我想拥有一个自己的钱包 - -> 作为一个新用户,我想创建自己的数字钱包,以便开始管理我的数字资产。 - -### Story 1.1: 首次启动引导 - -**场景**:用户首次打开 App - -**主线流程**: -1. 展示产品价值介绍(3页轮播) - - "便捷转账 - 随时随地管理资产" - - "多链支持 - 无限可能" - - "全面安全 - 资产更安全" -2. 点击"开始使用"或"我有钱包" -3. 进入创建/导入选择 - -**验收标准**: -- [ ] 轮播页最多 3 页,可跳过 -- [ ] 跳过后不再显示(记录到 localStorage) -- [ ] 支持左右滑动切换 - ---- - -### Story 1.2: 创建新钱包 - -**场景**:用户选择创建新钱包 - -**主线流程**: -``` -设置钱包锁 → 生成助记词 → 备份提示 → 验证助记词 → 创建成功 -``` - -**步骤详情**: - -| 步骤 | 用户操作 | 系统响应 | -|-----|---------|---------| -| 1 | 绘制图案(至少4点) | 实时显示连接点数 | -| 2 | 确认图案 | 检查一致性 | -| 3 | 点击下一步 | 生成 12 词助记词 | -| 4 | 点击"显示助记词" | 展示助记词(默认模糊) | -| 5 | 点击"我已备份" | 进入验证步骤 | -| 6 | 填写 3 个随机位置的词 | 验证正确性 | -| 7 | 点击"完成创建" | 派生多链地址,跳转首页 | - -**支线流程**: -- 用户点击"稍后备份" → 首页显示"未备份"警告 - -**异常处理**: - -| 异常 | 提示 | 处理 | -|-----|------|------| -| 图案点数不足 | "请至少连接4个点" | 禁用下一步 | -| 两次图案不一致 | "两次图案不一致" | 禁用下一步 | -| 助记词验证错误 | "第X个单词不正确" | 允许重试 | - ---- - -### Story 1.3: 导入已有钱包 - -**场景**:用户已有助记词或私钥 - -**助记词导入流程**: -``` -选择词数 → 输入助记词 → 设置钱包锁 → 导入成功 -``` - -**私钥导入流程**: -``` -选择链 → 输入私钥 → 设置钱包锁 → 导入成功 -``` - -**验收标准**: -- [ ] 支持 12/24 词助记词 -- [ ] 支持助记词粘贴自动分词 -- [ ] 私钥导入仅导入单链地址 -- [ ] 导入后派生所有支持的链地址 - ---- - -## Epic 2: 我想查看我的资产 - -> 作为钱包用户,我想随时查看我的资产情况,了解我有多少钱。 - -### Story 2.1: 查看资产总览 - -**场景**:用户登录后进入首页 - -**页面结构**: -``` -┌─────────────────────────────────┐ -│ [钱包名称 ▼] [链名称 ▼] │ ← 钱包/链切换 -├─────────────────────────────────┤ -│ │ -│ ┌───────────────────┐ │ -│ │ 钱包卡片 │ │ -│ │ 地址: 0x71C... │ │ -│ │ [复制] [二维码] │ │ -│ └───────────────────┘ │ -│ │ -│ [转账] [铸造] [收款] │ ← 快捷操作 -│ │ -├─────────────────────────────────┤ -│ [资产] [DP] │ ← Tab 切换 -├─────────────────────────────────┤ -│ ┌─────────────────────────┐ │ -│ │ ETH 10.5 ETH │ │ -│ │ USDT 1,000 USDT │ │ -│ │ ... │ │ -│ └─────────────────────────┘ │ -└─────────────────────────────────┘ -``` - -**验收标准**: -- [ ] 下拉刷新更新余额 -- [ ] 点击代币进入详情页 -- [ ] 支持切换资产/DP 视图 -- [ ] 离线时显示缓存数据 - ---- - -### Story 2.2: 切换钱包/链 - -**钱包切换**: -1. 点击钱包名称 -2. 底部弹出钱包列表 -3. 选择目标钱包 -4. 刷新首页数据 - -**链切换**: -1. 点击链名称 -2. 底部弹出链列表 -3. 选择目标链 -4. 刷新首页数据 - -**验收标准**: -- [ ] 保持最近选择的钱包/链 -- [ ] 切换时显示加载状态 -- [ ] 切换后余额自动更新 - ---- - -## Epic 3: 我想给别人转账 - -> 作为钱包用户,我想把代币转给别人,完成支付或转移。 - -### Story 3.1: 发起转账 - -**主线流程**: -``` -输入地址 → 输入金额 → 确认详情 → 验证钱包锁 → 发送成功 -``` - -**页面结构**: -``` -┌─────────────────────────────────┐ -│ ← 发送 │ -├─────────────────────────────────┤ -│ │ -│ 当前链: Ethereum │ -│ │ -│ 收款地址 │ -│ ┌─────────────────────────┐ │ -│ │ 输入或粘贴地址 [扫] │ │ -│ └─────────────────────────┘ │ -│ │ -│ 金额 │ -│ ┌─────────────────────────┐ │ -│ │ 0.00 [全部]│ │ -│ └─────────────────────────┘ │ -│ 可用: 10.5 ETH │ -│ │ -│ 网络费用: ~0.002 ETH │ -│ │ -│ ┌─────────────────────────┐ │ -│ │ 确认发送 │ │ -│ └─────────────────────────┘ │ -└─────────────────────────────────┘ -``` - -**异常处理**: - -| 异常 | 提示 | 处理 | -|-----|------|------| -| 地址格式错误 | "地址格式不正确" | 禁用发送 | -| 余额不足 | "余额不足" | 禁用发送 | -| 手续费不足 | "ETH 余额不足以支付手续费" | 提示充值 | -| 收款地址是自己 | "收款地址不能与发送地址相同" | 禁用发送 | - ---- - -### Story 3.2: 扫码转账 - -**主线流程**: -1. 点击扫码按钮 -2. 相机扫描二维码 -3. 解析地址(可能包含金额) -4. 自动填入表单 -5. 继续转账流程 - -**验收标准**: -- [ ] 支持从相册选择 -- [ ] 支持解析带金额的二维码 -- [ ] 扫描超时 60 秒提示 - ---- - -## Epic 4: 我想收款 - -> 作为钱包用户,我想让别人给我转账。 - -### Story 4.1: 展示收款码 - -**页面结构**: -``` -┌─────────────────────────────────┐ -│ ← 收款 │ -├─────────────────────────────────┤ -│ │ -│ ┌───────────────────┐ │ -│ │ │ │ -│ │ [二维码图片] │ │ -│ │ │ │ -│ └───────────────────┘ │ -│ │ -│ 0x71C7656EC7ab88b098defB... │ -│ │ -│ [复制地址] │ -│ │ -│ 当前链: Ethereum │ -│ │ -└─────────────────────────────────┘ -``` - -**验收标准**: -- [ ] 二维码包含完整地址 -- [ ] 支持复制地址 -- [ ] 支持保存二维码到相册 -- [ ] 可选设置收款金额 - ---- - -## Epic 5: 我想进行跨链质押 - -> 作为进阶用户,我想通过质押外链代币获取内链代币。 - -### Story 5.1: Mint 代币 - -**场景**:用户想用 USDT 铸造 USDM - -**主线流程**: -``` -选择质押代币 → 选择获取代币 → 输入数量 → 确认 → 等待完成 -``` - -**前置条件**: -- 钱包必须同时支持质押链和目标链 - -**验收标准**: -- [ ] 显示 1:1 兑换关系 -- [ ] 显示预估到账时间 -- [ ] 支持查看质押历史 - ---- - -## Epic 6: 我想保护我的钱包安全 - -> 作为用户,我希望我的钱包是安全的,不会被他人使用。 - -### Story 6.1: 设置应用锁 - -**主线流程**: -1. 进入设置 → 应用锁 -2. 开启钱包锁 -3. 绘制钱包锁图案验证 -4. 设置成功 - -**验收标准**: -- [ ] 支持钱包锁(九宫格图案锁) -- [ ] 支持指纹锁(需先开启钱包锁) -- [ ] 支持指纹支付 - ---- - -### Story 6.2: 备份助记词 - -**主线流程**: -1. 进入钱包管理 → 备份 -2. 绘制钱包锁图案验证 -3. 显示安全提示 -4. 展示助记词 -5. 完成确认验证 - -**验收标准**: -- [ ] 验证图案后才能查看 -- [ ] 提醒用户确保周围无人 -- [ ] 完成后消除"未备份"警告 - ---- - -## Epic 7: 我想管理常用地址 - -> 作为频繁转账的用户,我不想每次都手动输入地址。 - -### Story 7.1: 地址簿管理 - -**功能**: -- 添加地址(名称 + 链 + 地址) -- 编辑地址 -- 删除地址 -- 转账时选择地址 - -**验收标准**: -- [ ] 按链分类显示 -- [ ] 支持搜索 -- [ ] 转账成功后提示保存 - ---- - -## Epic 8: 我想与 DApp 交互 - -> 作为 Web3 用户,我想用钱包连接和使用 DApp。 - -### Story 8.1: 地址授权 - -**场景**:DApp 请求获取钱包地址 - -**流程**: -1. DApp 发起 `getAddress` 请求 -2. 钱包弹出授权页 -3. 显示 DApp 信息和请求内容 -4. 用户选择授权范围 -5. 验证钱包锁确认 -6. 返回地址信息 - -**授权类型**: -- 单钱包授权:当前钱包的所有链地址 -- 单链授权:指定链的所有钱包地址 -- 全部授权:所有钱包的所有地址 - ---- - -### Story 8.2: 签名授权 - -**签名类型**: -| 类型 | 场景 | 风险等级 | -|-----|------|---------| -| 消息签名 | 登录验证 | 低 | -| 转账签名 | 发起转账 | 高 | -| 合约签名 | 智能合约交互 | 高 | - -**验收标准**: -- [ ] 显示 DApp 来源信息 -- [ ] 转账签名显示完整交易详情 -- [ ] 高风险操作需验证钱包锁 - ---- - -## Epic 9: 我想个性化我的钱包 - -> 作为用户,我想按自己的偏好使用钱包。 - -### Story 9.1: 切换语言 - -**支持语言**: -- 简体中文 (zh-CN) -- 繁体中文 (zh-TW) -- English (en) -- 日本語 (ja) -- 한국어 (ko) -- العربية (ar) - RTL - -**验收标准**: -- [ ] 切换后立即生效 -- [ ] 记住用户选择 -- [ ] 首次自动检测系统语言 - ---- - -### Story 9.2: 设置货币单位 - -**支持货币**: -USD, CNY, EUR, JPY, KRW 等 - -**验收标准**: -- [ ] 首页显示换算后的法币价值 -- [ ] 汇率定期更新 -- [ ] 默认使用 USD - ---- - -## Epic 10: "我的"页面 - -> 作为用户,我需要一个统一的入口管理所有设置。 - -### 页面结构 - -``` -┌─────────────────────────────────┐ -│ 👤 钱包名称 │ -│ 0x71C7...976F │ -├─────────────────────────────────┤ -│ 📁 钱包管理 > │ -│ 📒 地址簿 > │ -├─────────────────────────────────┤ -│ 🔒 应用锁 > │ -│ 🔑 修改密码 > │ -│ 📝 备份助记词 🔴 > │ ← 未备份红点 -├─────────────────────────────────┤ -│ 🌐 语言 简体中文 > │ -│ 💰 货币单位 CNY > │ -│ ⛓️ 链配置 > │ -├─────────────────────────────────┤ -│ ❓ 帮助与反馈 > │ -│ ℹ️ 关于 > │ -└─────────────────────────────────┘ -``` - ---- - -## 本章小结 - -- 10 个 Epic 覆盖完整用户旅程 -- 每个 Story 包含主线流程、支线流程、异常处理 -- 用户心智考量贯穿每个设计决策 -- 验收标准明确,便于测试验证 - ---- - -## 下一篇 - -完成产品篇后,继续阅读 [第二篇:设计篇](../../02-设计篇/),了解产品的交互和视觉设计。 diff --git "a/docs/white-book/01-\344\272\247\345\223\201\347\257\207/index.md" "b/docs/white-book/01-\344\272\247\345\223\201\347\257\207/index.md" deleted file mode 100644 index cb87130d6..000000000 --- "a/docs/white-book/01-\344\272\247\345\223\201\347\257\207/index.md" +++ /dev/null @@ -1,56 +0,0 @@ -# 第一篇:产品篇 - -> 定义产品的「做什么」和「为什么」 - ---- - -## 概述 - -产品篇回答三个核心问题: - -1. **我们在做什么?** → [产品愿景](./01-产品愿景/) -2. **为谁而做?** → [用户画像](./02-用户画像/) -3. **具体做哪些功能?** → [用户故事](./03-用户故事/) - ---- - -## 章节导航 - -### [第一章:产品愿景](./01-产品愿景/) - -定义 BFM Pay 的核心定位和价值主张。 - -**要点**: -- 让每个人都能安全、简单地管理数字资产 -- 多链支持,一个钱包管理所有资产 -- 用户友好,复杂技术不成为障碍 - -### [第二章:用户画像](./02-用户画像/) - -深入分析目标用户群体及其需求。 - -**要点**: -- 三类核心用户:新手小白、进阶用户、活跃交易者 -- 用户心智模型:钱包 = 存钱的地方 -- 关键洞察:安全与便捷的平衡 - -### [第三章:用户故事](./03-用户故事/) - -定义完整的功能需求和验收标准。 - -**要点**: -- 10 个核心 Epic,覆盖完整用户旅程 -- 每个故事包含主线流程、支线流程、异常处理 -- 用户心智考量贯穿始终 - ---- - -## 本篇来源 - -本篇内容主要来源于 `PDR.md`(产品需求文档),并结合 mpay 原始代码的业务逻辑分析。 - ---- - -## 下一篇 - -完成产品篇的阅读后,建议继续阅读 [第二篇:设计篇](../02-设计篇/),了解产品的交互和视觉设计。 diff --git a/docs/white-book/02-Driver-Ref/00-Overview.md b/docs/white-book/02-Driver-Ref/00-Overview.md new file mode 100644 index 000000000..ef70e350a --- /dev/null +++ b/docs/white-book/02-Driver-Ref/00-Overview.md @@ -0,0 +1,49 @@ +# 00. 适配层架构 (Overview) + +Code: `src/services/chain-adapter/` + +为了支持多链 (EVM, UTXO, TVM),KeyApp 引入了一个抽象层,称为 **Driver Layer (或 HAL)**。 + +## 核心概念 + +### 1. ApiProvider (Driver) +每个 Provider 代表一个特定的数据源驱动。 +* 例如:`EtherscanProvider`, `EvmRpcProvider`, `MempoolProvider`。 +* 职责:负责与具体的后端 API 通信,并规范化数据格式。 + +### 2. ChainProvider (Manager) +每个链 (`chainId`) 拥有一个 `ChainProvider` 实例。 +* 职责:聚合该链配置下的所有 `ApiProvider`。 +* 策略:实现 **Fallback** 逻辑。如果 Etherscan 挂了,自动切换到 RPC。 + +### 3. Registry (Factory) +负责根据 `default-chains.json` 的配置,动态实例化对应的 Providers。 + +## 架构图 + +```mermaid +graph TD + User[UI / WalletStore] + + subgraph "ChainProvider (Manager)" + LB[Load Balancer / Fallback] + end + + subgraph "Drivers" + P1[Etherscan Provider] + P2[RPC Provider] + P3[WalletAPI Provider] + end + + subgraph "External World" + API1[api.etherscan.io] + API2[infura.io] + API3[walletapi.bfmeta.info] + end + + User --> LB + LB --> P1 & P2 & P3 + P1 --> API1 + P2 --> API2 + P3 --> API3 +``` diff --git a/docs/white-book/02-Driver-Ref/01-Protocol/01-Provider-Interface.md b/docs/white-book/02-Driver-Ref/01-Protocol/01-Provider-Interface.md new file mode 100644 index 000000000..2593bb2e9 --- /dev/null +++ b/docs/white-book/02-Driver-Ref/01-Protocol/01-Provider-Interface.md @@ -0,0 +1,55 @@ +# 01. Provider 接口定义 (ApiProvider Interface) + +Code: `src/services/chain-adapter/providers/types.ts` + +所有驱动必须实现此接口。它是系统的 HAL 标准。 + +## 接口定义 + +```typescript +export interface ApiProvider { + /** 驱动类型标识 (e.g., 'etherscan-v2') */ + readonly type: string; + + /** 服务端点 */ + readonly endpoint: string; + + /** 能力声明 (Capabilities) */ + readonly supportsNativeBalance?: boolean; + readonly supportsTransactionHistory?: boolean; + readonly supportsTokenBalances?: boolean; + + /** 核心方法 */ + getNativeBalance?(address: string): Promise; + getTokenBalances?(address: string): Promise; + getTransactionHistory?(address: string, limit?: number): Promise; + getTransaction?(hash: string): Promise; + + /** 交易方法 (可选) */ + estimateFee?(params: TransferParams): Promise; + broadcastTransaction?(signedTx: SignedTransaction): Promise; +} +``` + +## 关键数据结构 + +### Balance +```typescript +interface Balance { + amount: Amount; // 使用 BigInt 的金额封装 + symbol: string; +} +``` + +### Transaction +```typescript +interface Transaction { + hash: string; + from: string; + to: string; + value: string; + timestamp: number; + status: 'confirmed' | 'pending' | 'failed'; + direction: 'in' | 'out' | 'self'; +} +``` diff --git a/docs/white-book/02-Driver-Ref/02-EVM/01-RPC-Provider.md b/docs/white-book/02-Driver-Ref/02-EVM/01-RPC-Provider.md new file mode 100644 index 000000000..b61c4d68c --- /dev/null +++ b/docs/white-book/02-Driver-Ref/02-EVM/01-RPC-Provider.md @@ -0,0 +1,21 @@ +# 01. EVM RPC Provider + +Code: `src/services/chain-adapter/providers/evm-rpc-provider.ts` + +基于标准 JSON-RPC 规范实现的通用驱动。 + +## 支持能力 +* ✅ `getNativeBalance`: 使用 `eth_getBalance` +* ✅ `getBlockHeight`: 使用 `eth_blockNumber` +* ❌ `getTransactionHistory`: 标准 RPC 不支持高效的历史查询 (需要 Indexer) + +## 典型配置 +```json +{ + "type": "ethereum-rpc", + "endpoint": "https://ethereum-rpc.publicnode.com" +} +``` + +## 实现细节 +该 Provider 主要作为 **Fallback** 使用。当 Etherscan 或 Indexer 不可用时,它能保证用户至少能看到余额,虽然看不到交易记录。 diff --git a/docs/white-book/02-Driver-Ref/02-EVM/02-Etherscan-Provider.md b/docs/white-book/02-Driver-Ref/02-EVM/02-Etherscan-Provider.md new file mode 100644 index 000000000..e738cda2d --- /dev/null +++ b/docs/white-book/02-Driver-Ref/02-EVM/02-Etherscan-Provider.md @@ -0,0 +1,20 @@ +# 02. Etherscan Provider + +Code: `src/services/chain-adapter/providers/etherscan-provider.ts` + +适配 Etherscan V2 API 及其兼容实现 (如 Blockscout)。 + +## 支持能力 +* ✅ `getNativeBalance` +* ✅ `getTransactionHistory`: 聚合 `txlist` (原生) 和 `tokentx` (ERC20) +* ✅ `getTokenBalances`: (部分支持) + +## 聚合逻辑 +Etherscan 将原生交易和代币交易分开返回。驱动层负责将它们聚合: +1. 并行请求 `txlist` 和 `tokentx`。 +2. 按 `hash` 进行分组。 +3. 如果一个 `hash` 同时存在于两者,合并为一个 `Transaction` 对象,并在 `assets` 数组中包含原生资产和代币资产。 +4. 按时间戳降序排列。 + +## API Key 管理 +支持轮询多个 API Key,并自动处理限流 (429) 错误。 diff --git a/docs/white-book/02-Driver-Ref/README.md b/docs/white-book/02-Driver-Ref/README.md new file mode 100644 index 000000000..92007d6ca --- /dev/null +++ b/docs/white-book/02-Driver-Ref/README.md @@ -0,0 +1,18 @@ +# 📘 Book T2: The Driver Reference (驱动技术参考) + +> **The Hardware Abstraction Layer (HAL) for Blockchains** +> +> 本书定义了 KeyApp 的多链适配器架构,并详细记录了所有已支持链的驱动实现细节。 + +## 📖 目录 + +* [00-Overview.md](./00-Overview.md) - 适配层架构概览 +* **01-Protocol (核心协议)** + * [01-Provider-Interface.md](./01-Protocol/01-Provider-Interface.md) - ApiProvider 接口定义 +* **02-EVM (以太坊生态)** + * [01-RPC-Provider.md](./02-EVM/01-RPC-Provider.md) - JSON-RPC 实现 + * [02-Etherscan-Provider.md](./02-EVM/02-Etherscan-Provider.md) - Etherscan 实现 +* **03-UTXO (比特币生态)** + * [01-Mempool-Provider.md](./03-UTXO/01-Mempool-Provider.md) - Mempool.space 实现 +* **04-TVM (波场生态)** + * [01-Tron-Provider.md](./04-TVM/01-Tron-Provider.md) - TronGrid 实现 diff --git "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/01-\344\272\244\344\272\222\350\256\276\350\256\241/index.md" "b/docs/white-book/02-\350\256\276\350\256\241\347\257\207/01-\344\272\244\344\272\222\350\256\276\350\256\241/index.md" deleted file mode 100644 index fb1d37b56..000000000 --- "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/01-\344\272\244\344\272\222\350\256\276\350\256\241/index.md" +++ /dev/null @@ -1,359 +0,0 @@ -# 第四章:交互设计 - -> 定义信息架构、导航流程和页面结构 - ---- - -## 4.1 信息架构 - -### 一级架构 - -``` -BFM Pay -├── 首页 (Home) ← 默认落地页 -│ ├── 钱包卡片 -│ ├── 快捷操作 -│ └── 资产列表 -│ -├── 历史 (History) -│ └── 交易记录列表 -│ -└── 设置 (Settings) - ├── 钱包管理 - ├── 安全设置 - ├── 偏好设置 - └── 关于 -``` - -### 二级页面 - -``` -首页延伸 -├── 发送 (Send) -├── 收款 (Receive) -├── 铸造 (Mint) -├── 代币详情 (Token Detail) -└── 钱包详情 (Wallet Detail) - -设置延伸 -├── 钱包列表 (Wallet List) -├── 创建钱包 (Create Wallet) -├── 导入钱包 (Import Wallet) -├── 地址簿 (Address Book) -├── 语言设置 (Language) -├── 货币设置 (Currency) -├── 链配置 (Chain Config) -└── 应用锁 (App Lock) - -授权相关 -├── 地址授权 (Authorize Address) -└── 签名授权 (Authorize Signature) -``` - ---- - -## 4.2 导航模式 - -### 底部 Tab 导航 - -``` -┌─────────────────────────────────────┐ -│ │ -│ 页面内容区域 │ -│ │ -├─────────────────────────────────────┤ -│ 🏠 📜 ⚙️ │ -│ 首页 历史 设置 │ -└─────────────────────────────────────┘ -``` - -**设计决策**: -- 3 个 Tab,保持简洁 -- 首页为默认选中 -- Tab 图标 + 文字,便于识别 - -### 栈式导航 (Stackflow) - -``` - push -首页 ────────────────────► 发送页面 - pop -首页 ◄──────────────────── 发送页面 -``` - -**特点**: -- 原生 App 体验 -- 支持手势返回 -- 保留页面状态 - -### 底部弹窗 (Bottom Sheet) - -**使用场景**: -- 选择器(钱包、链、代币) -- 确认框(转账确认) -- 操作菜单 - -``` -┌─────────────────────────────────────┐ -│ │ -│ 页面内容 │ -│ │ -├─────────────────────────────────────┤ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ ← 拖动条 -│ │ -│ 选择链 │ -│ │ -│ ┌───────────────────────────────┐ │ -│ │ ◉ Ethereum │ │ -│ │ ○ BSC │ │ -│ │ ○ Tron │ │ -│ │ ○ BFMeta │ │ -│ └───────────────────────────────┘ │ -│ │ -└─────────────────────────────────────┘ -``` - ---- - -## 4.3 页面结构模板 - -### 标准页面结构 - -``` -┌─────────────────────────────────────┐ -│ ← 返回 页面标题 [操作按钮] │ ← App Bar -├─────────────────────────────────────┤ -│ │ -│ │ -│ 主要内容区域 │ ← Content -│ │ -│ │ -├─────────────────────────────────────┤ -│ [主要操作按钮] │ ← Footer (可选) -└─────────────────────────────────────┘ -``` - -### 表单页面结构 - -``` -┌─────────────────────────────────────┐ -│ ← 返回 发送 │ -├─────────────────────────────────────┤ -│ │ -│ 标签 │ -│ ┌───────────────────────────────┐ │ -│ │ 输入框 │ │ -│ └───────────────────────────────┘ │ -│ 提示或错误信息 │ -│ │ -│ 标签 │ -│ ┌───────────────────────────────┐ │ -│ │ 输入框 │ │ -│ └───────────────────────────────┘ │ -│ │ -├─────────────────────────────────────┤ -│ ┌───────────────────────────────┐ │ -│ │ 确认发送 │ │ -│ └───────────────────────────────┘ │ -└─────────────────────────────────────┘ -``` - -### 列表页面结构 - -``` -┌─────────────────────────────────────┐ -│ ← 返回 交易历史 │ -├─────────────────────────────────────┤ -│ ┌───────────────────────────────┐ │ -│ │ 🔍 搜索 │ │ -│ └───────────────────────────────┘ │ -├─────────────────────────────────────┤ -│ 今天 │ -│ ├── 交易记录 1 │ -│ ├── 交易记录 2 │ -│ 昨天 │ -│ ├── 交易记录 3 │ -│ └── ... │ -└─────────────────────────────────────┘ -``` - ---- - -## 4.4 核心流程设计 - -### 创建钱包流程 - -``` -┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ -│ 设置钱包锁│───►│生成助记词│───►│验证助记词│───►│ 创建成功 │ -└─────────┘ └─────────┘ └─────────┘ └─────────┘ - │ │ │ - ▼ ▼ ▼ - 图案确认 显示/隐藏 3词验证 -``` - -**步骤指示器**: -``` - ●────────○────────○ - 图案 备份 验证 -``` - -### 转账流程 - -``` -┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ -│ 输入信息 │───►│ 确认详情 │───►│ 验证钱包锁│───►│ 发送成功 │ -└─────────┘ └─────────┘ └─────────┘ └─────────┘ - │ │ │ - ▼ ▼ ▼ - 地址+金额 显示手续费 图案/指纹 -``` - -### DWEB 授权流程 - -``` - DApp 请求 钱包响应 - │ │ - ▼ │ -┌───────────┐ │ -│ getAddress│──────────────┼──► 弹出授权页 -└───────────┘ │ │ - │ ▼ - │ 选择授权范围 - │ │ - │ ▼ - │ 验证钱包锁确认 - │ │ - ◄─────────┘ - 返回地址数据 -``` - ---- - -## 4.5 手势交互 - -### 支持的手势 - -| 手势 | 场景 | 行为 | -|-----|------|------| -| 左滑返回 | 所有次级页面 | 返回上一页 | -| 下拉刷新 | 首页、列表页 | 刷新数据 | -| 下滑关闭 | 底部弹窗 | 关闭弹窗 | -| 长按 | 地址文本 | 复制地址 | -| 双击 | 金额输入框 | 填入最大值 | - -### 手势反馈 - -- 滑动时有视觉位移 -- 刷新时显示加载指示器 -- 操作完成有触觉反馈(DWEB) - ---- - -## 4.6 加载状态设计 - -### 页面加载 - -``` -┌─────────────────────────────────────┐ -│ ← 返回 代币详情 │ -├─────────────────────────────────────┤ -│ │ -│ ┌─────────────┐ │ -│ │ ◠ ◡ ◠ │ │ -│ │ 加载中... │ │ -│ └─────────────┘ │ -│ │ -└─────────────────────────────────────┘ -``` - -### 骨架屏 - -``` -┌─────────────────────────────────────┐ -│ ████████ ████ │ -├─────────────────────────────────────┤ -│ ┌───────────────────────────────┐ │ -│ │ ████████████████████████████ │ │ -│ │ ████████ ░░░░░░░░░░░░░░░░░░ │ │ -│ └───────────────────────────────┘ │ -│ │ -│ ████ ░░░░░░░░░░ ████ ░░░░░░░░░ │ -│ ████ ░░░░░░░░░░ ████ ░░░░░░░░░ │ -└─────────────────────────────────────┘ -``` - -### 操作中状态 - -``` -┌───────────────────────────────────┐ -│ │ -│ ◠ ◡ ◠ │ -│ │ -│ 交易发送中... │ -│ │ -│ 请勿关闭页面 │ -│ │ -└───────────────────────────────────┘ -``` - ---- - -## 4.7 错误状态设计 - -### 空状态 - -``` -┌─────────────────────────────────────┐ -│ │ -│ 📭 │ -│ │ -│ 暂无交易记录 │ -│ │ -│ 完成您的第一笔转账后 │ -│ 交易记录将显示在这里 │ -│ │ -└─────────────────────────────────────┘ -``` - -### 网络错误 - -``` -┌─────────────────────────────────────┐ -│ │ -│ 📡 │ -│ │ -│ 网络连接失败 │ -│ │ -│ 请检查网络后重试 │ -│ │ -│ [重试] │ -│ │ -└─────────────────────────────────────┘ -``` - -### 表单错误 - -``` - 收款地址 - ┌───────────────────────────────┐ - │ 0x71C7656... │ - └───────────────────────────────┘ - ⚠️ 地址格式不正确 -``` - ---- - -## 本章小结 - -- 信息架构分 3 个一级入口,层级清晰 -- 导航采用底部 Tab + 栈式导航 + 底部弹窗 -- 页面结构有标准模板,保持一致性 -- 核心流程设计注重用户引导 -- 加载和错误状态设计完善 - ---- - -## 下一章 - -继续阅读 [第五章:视觉设计](../02-视觉设计/),了解视觉系统和品牌风格。 diff --git "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/dark-mode.md" "b/docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/dark-mode.md" deleted file mode 100644 index f872505e7..000000000 --- "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/dark-mode.md" +++ /dev/null @@ -1,162 +0,0 @@ -# 暗色模式最佳实践 - -> 确保应用在浅色和暗色主题下都有良好的可读性和视觉体验。 - ---- - -## 核心原则 - -### 1. 使用语义化颜色 Token - -**始终优先使用 shadcn/ui 的语义化颜色变量**,它们会自动适配主题: - -```tsx -// ✅ 正确 - 使用语义化颜色 -
-

辅助文字

- -
- -// ❌ 错误 - 硬编码颜色 -
-

辅助文字

-
-``` - -### 2. 配色对必须配套使用 - -每个 shadcn/ui 颜色变量都有对应的 `-foreground` 变体: - -| 背景色 | 前景色 | 使用场景 | -|--------|--------|----------| -| `bg-primary` | `text-primary-foreground` | 主要按钮 | -| `bg-secondary` | `text-secondary-foreground` | 次要按钮 | -| `bg-destructive` | `text-destructive-foreground` | 危险按钮 | -| `bg-muted` | `text-muted-foreground` | 低调区域 | -| `bg-accent` | `text-accent-foreground` | 强调区域 | -| `bg-card` | `text-card-foreground` | 卡片容器 | - -**例外**:`text-muted-foreground` 可以单独用于次要文字,不需要配套 `bg-muted`。 - -### 3. 禁止 `bg-primary text-white` - -```tsx -// ❌ 错误 - text-white 在暗色模式下不会变色 - - -// ✅ 正确 - text-primary-foreground 会自动适配主题 - -``` - -**原因**:在暗色模式下,`--primary` 变亮而 `--primary-foreground` 变暗,保持对比度。`text-white` 始终是白色,在亮色的 `bg-primary` 上可能对比度不足。 - -### 4. 不要将背景色用作文字色 - -```tsx -// ❌ 错误 - secondary 是背景色 -

看不见的文字

- -// ✅ 正确 -

次要文字

-// 或用于按钮 - -``` - -### 5. `bg-muted` 必须指定文字颜色 - -```tsx -// ❌ 错误 - 没有文字颜色,可能继承错误的颜色 -
- 这段文字可能不可见 -
- -// ✅ 正确 -
- 清晰可见 -
-``` - ---- - -## 硬编码颜色规则 - -### 需要 dark: 变体的情况 - -使用硬编码灰色时,**必须**添加暗色变体: - -```tsx -// ✅ 正确 - 有 dark: 变体 -
...
-

...

-
...
- -// ❌ 错误 - 缺少 dark: 变体 -
...
-``` - -### 灰度映射参考 - -| 浅色 | 暗色 | -|------|------| -| gray-50 | gray-900 | -| gray-100 | gray-800 | -| gray-200 | gray-700 | -| gray-300 | gray-600 | -| gray-400 | gray-500 | -| gray-500 | gray-400 | - -### 可接受的例外 - -以下情况**不需要** dark: 变体: - -1. **半透明颜色在渐变/彩色背景上**: - ```tsx - // ✅ OK - 在渐变背景上 -
- 半透明装饰 -

白色文字

-
- ``` - -2. **相机/扫描器界面**: - ```tsx - // ✅ OK - 相机背景 -
-
- ``` - -3. **固定颜色背景上的白字**(如 `bg-green-500`、`bg-red-500`): - ```tsx - // ✅ OK - 固定颜色背景 -
成功
- ``` - -4. **开发工具** (`mock-devtools/`):规则可以放宽 - ---- - -## 自动检查 - -运行主题检查: - -```bash -pnpm theme:check -``` - -检查规则: - -| 规则 | 严重性 | 说明 | -|------|--------|------| -| `no-bg-as-text` | Error | 禁止将背景色用作文字色 | -| `text-white-on-semantic-bg` | Error | 禁止在语义背景色上使用 text-white | -| `orphan-foreground` | Warning | foreground 颜色应有配套背景 | -| `bg-muted-no-text-color` | Warning | bg-muted 需要明确的文字颜色 | -| `missing-dark-variant` | Warning | 硬编码灰色应有 dark: 变体 | - ---- - -## 相关文档 - -- [主题配色系统](./theme-colors.md) - 配色对的详细说明 -- [shadcn/ui 主题文档](https://ui.shadcn.com/docs/theming) diff --git "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/index.md" "b/docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/index.md" deleted file mode 100644 index d6e6a545e..000000000 --- "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/index.md" +++ /dev/null @@ -1,374 +0,0 @@ -# 第五章:视觉设计 - -> 定义视觉系统和品牌风格 - ---- - -## 5.1 设计令牌 (Design Tokens) - -### 颜色系统 - -**品牌色** - -```css -/* 主色 - Bioforest Green */ ---primary: oklch(0.6 0.13 163); ---primary-foreground: oklch(0.98 0.02 166); - -/* 深色模式 */ -.dark { - --primary: oklch(0.7 0.15 162); - --primary-foreground: oklch(0.26 0.05 173); -} -``` - -**语义色** - -| 用途 | 浅色模式 | 深色模式 | 使用场景 | -|-----|---------|---------|---------| -| 成功 | `green-500` | `green-400` | 交易成功、验证通过 | -| 警告 | `yellow-500` | `yellow-400` | 提醒、注意事项 | -| 错误 | `red-500` | `red-400` | 错误提示、危险操作 | -| 信息 | `blue-500` | `blue-400` | 一般提示 | - -**中性色** - -```css ---background: oklch(1 0 0); /* 页面背景 */ ---foreground: oklch(0.145 0 0); /* 主要文字 */ ---muted: oklch(0.97 0 0); /* 次级背景 */ ---muted-foreground: oklch(0.556 0 0); /* 次级文字 */ ---border: oklch(0.922 0 0); /* 边框 */ -``` - -### 字体系统 - -**字体家族** - -| 字体 | CSS 类 | 用途 | -|-----|--------|------| -| DM Sans | `font-sans` | UI 文本、按钮、标签(默认) | -| DM Mono | `font-mono` | 地址、金额、代码 | -| DM Serif Display | `font-serif` | 协议、提示、正式内容 | - -**字体大小** - -| 级别 | 大小 | 行高 | 使用场景 | -|-----|------|------|---------| -| xs | 12px | 16px | 辅助信息、时间戳 | -| sm | 14px | 20px | 次要文字、标签 | -| base | 16px | 24px | 正文、按钮文字 | -| lg | 18px | 28px | 小标题 | -| xl | 20px | 28px | 页面标题 | -| 2xl | 24px | 32px | 金额显示 | -| 3xl | 30px | 36px | 大额数字 | - -### 间距系统 - -```css -/* 基于 4px 网格 */ ---spacing-1: 4px; ---spacing-2: 8px; ---spacing-3: 12px; ---spacing-4: 16px; ---spacing-5: 20px; ---spacing-6: 24px; ---spacing-8: 32px; ---spacing-10: 40px; ---spacing-12: 48px; -``` - -**使用规范**: -- 组件内部间距:4px, 8px -- 组件之间间距:16px, 24px -- 区块之间间距:32px, 48px - -### 圆角系统 - -| 用途 | 大小 | 使用场景 | -|-----|------|---------| -| sm | 4px | 小按钮、标签 | -| md | 8px | 输入框、卡片 | -| lg | 12px | 大卡片、弹窗 | -| xl | 16px | 底部弹窗 | -| full | 9999px | 圆形按钮、头像 | - ---- - -## 5.2 组件样式 - -### 按钮 - -**变体** - -``` -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ Primary │ │ Secondary │ │ Outline │ -│ 主要操作 │ │ 次要操作 │ │ 边框按钮 │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ Ghost │ │ Destructive │ │ Link │ -│ 幽灵按钮 │ │ 危险操作 │ │ 链接按钮 │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ -``` - -**尺寸** - -| 尺寸 | 高度 | 内边距 | 字体大小 | -|-----|------|-------|---------| -| sm | 32px | 12px | 14px | -| md | 40px | 16px | 14px | -| lg | 48px | 20px | 16px | - -**渐变按钮** - -```css -.gradient-blue { - background: linear-gradient(180deg, #6de7fe 0%, #44b5f7 100%); -} -.gradient-purple { - background: linear-gradient(180deg, #a694f8 0%, #8970ff 100%); -} -.gradient-red { - background: linear-gradient(180deg, #f77fa2 0%, #ea4879 100%); -} -``` - -### 卡片 - -**钱包卡片** - -``` -┌─────────────────────────────────────┐ -│ │ -│ ┌──┐ 钱包名称 │ -│ │🔷│ 0x71C7...976F [复制] │ -│ └──┘ │ -│ │ -│ ═══════════════════════════════ │ -│ │ -│ 转账 铸造 收款 │ -│ │ -└─────────────────────────────────────┘ -``` - -样式特点: -- 圆角 16px -- 阴影 `shadow-md` -- 背景渐变或纯色 - -**资产卡片** - -``` -┌─────────────────────────────────────┐ -│ ┌──┐ │ -│ │🔶│ ETH 10.5 │ -│ └──┘ Ethereum $2,100│ -└─────────────────────────────────────┘ -``` - -### 输入框 - -**默认状态** - -``` -┌───────────────────────────────────┐ -│ 请输入地址 │ -└───────────────────────────────────┘ -``` - -**聚焦状态** - -``` -┌───────────────────────────────────┐ -│ 0x71C7656EC7ab88b098defB751B... │ ← 边框高亮 -└───────────────────────────────────┘ -``` - -**错误状态** - -``` -┌───────────────────────────────────┐ -│ invalid_address │ ← 红色边框 -└───────────────────────────────────┘ - ⚠️ 地址格式不正确 ← 错误提示 -``` - -### 底部弹窗 - -``` -┌─────────────────────────────────────┐ -│ │ -│ (页面内容变暗) │ -│ │ -├─────────────────────────────────────┤ -│ ━━━━━━━━━━━ │ ← 拖动指示条 -│ │ -│ 选择链 │ ← 标题 -│ │ -│ ○ Ethereum │ -│ ● BSC │ ← 当前选中 -│ ○ Tron │ -│ ○ BFMeta │ -│ │ -│ ┌───────────────────────────────┐ │ -│ │ 确定 │ │ ← 操作按钮(可选) -│ └───────────────────────────────┘ │ -│ │ -└─────────────────────────────────────┘ -``` - ---- - -## 5.3 图标系统 - -### 图标来源 - -使用 [Tabler Icons](https://tabler.io/icons),React 组件:`@tabler/icons-react` - -### 常用图标 - -| 功能 | 图标 | 名称 | -|-----|------|------| -| 返回 | ← | `IconArrowLeft` | -| 复制 | 📋 | `IconCopy` | -| 扫码 | 📷 | `IconScan` | -| 设置 | ⚙️ | `IconSettings` | -| 钱包 | 💼 | `IconWallet` | -| 发送 | ↗️ | `IconSend` | -| 接收 | ↙️ | `IconDownload` | -| 锁定 | 🔒 | `IconLock` | -| 警告 | ⚠️ | `IconAlertTriangle` | -| 成功 | ✅ | `IconCheck` | - -### 图标尺寸 - -| 场景 | 尺寸 | 使用 | -|-----|------|------| -| 按钮内图标 | 16px | 小型按钮 | -| 列表项图标 | 20px | 列表左侧图标 | -| 导航图标 | 24px | Tab Bar | -| 状态图标 | 48px | 空状态、结果页 | - ---- - -## 5.4 深色模式 - -### 颜色映射 - -| 元素 | 浅色模式 | 深色模式 | -|-----|---------|---------| -| 背景 | `#ffffff` | `#0a0a0a` | -| 前景 | `#0a0a0a` | `#fafafa` | -| 卡片 | `#ffffff` | `#171717` | -| 边框 | `#e5e5e5` | `#262626` | -| 次级文字 | `#737373` | `#a3a3a3` | - -### 实现方式 - -```css -@custom-variant dark (&:is(.dark *)); - -:root { - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); -} - -.dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); -} -``` - -### 切换逻辑 - -1. 默认跟随系统 -2. 用户可手动切换 -3. 存储用户偏好 - ---- - -## 5.5 响应式设计 - -### 断点定义 - -| 断点 | 宽度 | 设备 | -|-----|------|------| -| xs | < 360px | 小型手机 | -| sm | 360-414px | 标准手机 | -| md | 414-600px | 大屏手机 | -| lg | 600-840px | 折叠屏展开 | -| xl | > 840px | 平板 | - -### 容器查询 - -组件使用容器查询实现自适应: - -```tsx -
-
320px:水平布局 - "@md:p-6" // >448px:更大内边距 - )}> - {/* 内容 */} -
-
-``` - ---- - -## 5.6 动画规范 - -### 时长 - -| 类型 | 时长 | 使用场景 | -|-----|------|---------| -| 快速 | 150ms | hover、focus | -| 标准 | 200ms | 展开、收起 | -| 中等 | 300ms | 页面过渡 | -| 慢速 | 500ms | 复杂动画 | - -### 缓动函数 - -```css ---ease-out: cubic-bezier(0, 0, 0.2, 1); ---ease-in: cubic-bezier(0.4, 0, 1, 1); ---ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); -``` - -### 常用动画 - -**淡入** -```css -@keyframes fade-in { - from { opacity: 0; } - to { opacity: 1; } -} -``` - -**滑入** -```css -@keyframes slide-in-from-bottom { - from { transform: translateY(100%); } - to { transform: translateY(0); } -} -``` - ---- - -## 本章小结 - -- 设计令牌定义了颜色、字体、间距、圆角 -- 组件样式包括按钮、卡片、输入框、弹窗 -- 图标使用 Tabler Icons -- 支持深色模式 -- 响应式设计使用容器查询 -- 动画规范保证一致的交互体验 - ---- - -## 下一章 - -继续阅读 [第六章:设计原则](../03-设计原则/),了解设计决策的指导原则。 diff --git "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/theme-colors.md" "b/docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/theme-colors.md" deleted file mode 100644 index f9239d3ae..000000000 --- "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/02-\350\247\206\350\247\211\350\256\276\350\256\241/theme-colors.md" +++ /dev/null @@ -1,345 +0,0 @@ -# 主题配色系统 - -> shadcn/ui 配色对的正确使用方式 - ---- - -## 核心概念:配色对 - -shadcn/ui 使用**配色对**设计,每个颜色变量都有对应的 `xxx-foreground`: - -| 变量 | 用途 | -|------|------| -| `--xxx` | **背景色** | -| `--xxx-foreground` | **在该背景上的前景色/文字色** | - -**关键规则**:它们必须**配套使用**。 - ---- - -## 配色对一览 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 配色对使用指南 │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ primary ──────────────────────► 主要操作 │ -│ ┌─────────────────────────────────────┐ │ -│ │ bg-primary text-primary-foreground │ 主要按钮 │ -│ └─────────────────────────────────────┘ │ -│ ┌─────────────────────────────────────┐ │ -│ │ text-primary │ 强调文字/链接 │ -│ └─────────────────────────────────────┘ │ -│ │ -│ secondary ──────────────────────► 次要操作 │ -│ ┌─────────────────────────────────────┐ │ -│ │ bg-secondary text-secondary-foreground │ 次要按钮 │ -│ └─────────────────────────────────────┘ │ -│ │ -│ muted ──────────────────────► 低调/辅助 │ -│ ┌─────────────────────────────────────┐ │ -│ │ bg-muted text-muted-foreground │ 低调背景区域 │ -│ └─────────────────────────────────────┘ │ -│ ┌─────────────────────────────────────┐ │ -│ │ text-muted-foreground │ 次要/辅助文字 │ -│ └─────────────────────────────────────┘ │ -│ │ -│ destructive ──────────────────────► 危险/删除 │ -│ ┌─────────────────────────────────────┐ │ -│ │ bg-destructive text-destructive-foreground │ 危险按钮 │ -│ └─────────────────────────────────────┘ │ -│ ┌─────────────────────────────────────┐ │ -│ │ text-destructive │ 错误文字 │ -│ └─────────────────────────────────────┘ │ -│ │ -│ accent ──────────────────────► 强调/高亮 │ -│ ┌─────────────────────────────────────┐ │ -│ │ bg-accent text-accent-foreground │ 悬停/选中状态 │ -│ └─────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 正确用法示例 - -### ✅ 主要按钮 - -```tsx - -``` - -### ✅ 次要按钮 - -```tsx - -``` - -### ✅ 次要/辅助文字 - -```tsx -

- 这是辅助说明文字 -

-``` - -### ✅ 强调文字/链接 - -```tsx - - 查看详情 - -``` - -### ✅ 错误提示 - -```tsx -

- 图案不正确 -

-``` - -### ✅ 成功/确认状态 - -```tsx - -交易成功 -``` - ---- - -## 常见错误 - -### ❌ 错误:text-secondary 当文字用 - -```tsx -// ❌ 错误!secondary 是背景色,在亮色主题下几乎不可见 -

这段文字看不见

- -// ✅ 正确 -

次要文字

-``` - -### ❌ 错误:用 secondary 表示成功状态 - -```tsx -// ❌ 错误!secondary 是中性灰色,不代表成功 - - -// ✅ 正确 - -``` - -### ❌ 错误:单独使用 foreground 类 - -```tsx -// ❌ 错误!foreground 颜色是为特定背景设计的 -
- 这个白色文字在默认背景上看不清 -
- -// ✅ 正确:配套使用 -
- 这样才对 -
-``` - ---- - -## 语义色对照表 - -| 语义 | 正确用法 | 使用场景 | -|------|----------|----------| -| 主要操作 | `bg-primary text-primary-foreground` | 主要按钮 | -| 主要强调 | `text-primary` | 链接、强调文字 | -| 次要操作 | `bg-secondary text-secondary-foreground` | 次要按钮 | -| 次要文字 | `text-muted-foreground` | 辅助说明、时间戳 | -| 危险操作 | `bg-destructive text-destructive-foreground` | 删除按钮 | -| 错误提示 | `text-destructive` | 错误信息 | -| 成功状态 | `text-green-500` | 完成、确认 | -| 警告状态 | `text-yellow-500` | 警告、注意 | -| 信息提示 | `text-blue-500` | 一般提示 | - ---- - -## 当前主题变量值 - -### 亮色主题 - -```css -:root { - --primary: oklch(0.59 0.26 323); /* 品牌紫色 */ - --primary-foreground: oklch(1 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); /* 极浅灰(同 secondary) */ - --muted-foreground: oklch(0.552 0.016 285.938); /* 中灰文字 */ - - --destructive: oklch(0.577 0.245 27.325); /* 红色 */ - --destructive-foreground: oklch(1 0 0); /* 白色 */ -} -``` - -### 暗色主题 - -```css -.dark { - --primary: oklch(0.67 0.26 322); /* 亮一点的品牌紫色 */ - --primary-foreground: oklch(0.141 0.005 285.823); /* 深色 */ - - --secondary: oklch(0.274 0.006 286.033); /* 深灰(背景) */ - --secondary-foreground: oklch(0.985 0 0); /* 白色文字 */ - - --muted: oklch(0.274 0.006 286.033); /* 深灰(同 secondary) */ - --muted-foreground: oklch(0.705 0.015 286.067); /* 浅灰文字 */ - - --destructive: oklch(0.704 0.191 22.216); /* 亮红色 */ - --destructive-foreground: oklch(1 0 0); /* 白色 */ -} -``` - ---- - -## 快速参考 - -| 我想要... | 使用 | -|-----------|------| -| 主要按钮 | `bg-primary text-primary-foreground` | -| 次要按钮 | `bg-secondary text-secondary-foreground` | -| 危险按钮 | `bg-destructive text-destructive-foreground` | -| 强调链接 | `text-primary` | -| 辅助文字 | `text-muted-foreground` | -| 错误文字 | `text-destructive` | -| 成功图标 | `text-green-500` | -| 低调背景 | `bg-muted` | -| 卡片背景 | `bg-card text-card-foreground` | - ---- - -## 本节小结 - -1. **配色对必须配套使用**:`bg-xxx` 搭配 `text-xxx-foreground` -2. **不要单独使用 `text-secondary`**:它是背景色,不是文字色 -3. **次要文字用 `text-muted-foreground`**:这才是设计给辅助文字的颜色 -4. **成功/确认状态用 `text-green-500`**:不要用 secondary -5. **理解亮/暗模式差异**:颜色值会自动切换,保持对比度 - ---- - -## 钱包个性化主题色 - -### 概述 - -每个钱包拥有独立的主题色(色相值 0-360),用于: -- 钱包卡片渐变背景 -- 确认/保存按钮动态颜色 -- 视觉区分多个钱包 - -### CSS 自定义属性 - -```css -@property --primary-hue { - syntax: ""; - inherits: true; - initial-value: 323; -} - -@property --primary-lightness { - syntax: ""; - inherits: true; - initial-value: 0.59; -} - -@property --primary-saturation { - syntax: ""; - inherits: true; - initial-value: 0.26; -} -``` - -### 动态主题色应用 - -```tsx -// 按钮跟随钱包主题色 - -``` - -### 色相派生算法 - -从钱包地址稳定派生初始色相,确保跨设备一致: - -```typescript -function deriveThemeHue(address: string): number { - let hash = 0; - for (let i = 0; i < address.length; i++) { - hash = (hash << 5) - hash + address.charCodeAt(i); - hash = hash & hash; - } - return ((hash % 360) + 360) % 360; -} -``` - -### 预设颜色 - -提供 12 个预设色相,覆盖色轮主要区域: - -```typescript -const WALLET_THEME_COLORS = [ - { hue: 0, name: '红色' }, - { hue: 30, name: '橙色' }, - { hue: 60, name: '黄色' }, - { hue: 90, name: '黄绿' }, - { hue: 120, name: '绿色' }, - { hue: 150, name: '青绿' }, - { hue: 180, name: '青色' }, - { hue: 210, name: '天蓝' }, - { hue: 240, name: '蓝色' }, - { hue: 270, name: '紫色' }, - { hue: 300, name: '品红' }, - { hue: 330, name: '玫红' }, -]; -``` - -### 趋避算法 - -为避免多钱包颜色相近,选择器会: -1. 显示已用色相标记 -2. 降低相近颜色(距离 < 30°)的视觉权重 - -```typescript -function calculateAvoidanceWeight(hue: number, existingHues: number[]): number { - const minDistance = 30; - let minDist = 180; - - for (const existing of existingHues) { - const dist = Math.min(Math.abs(hue - existing), 360 - Math.abs(hue - existing)); - if (dist < minDist) minDist = dist; - } - - return minDist < minDistance ? minDist / minDistance : 1; -} -``` - ---- - -## 相关链接 - -- [shadcn/ui 主题文档](https://ui.shadcn.com/docs/theming) -- [Tailwind CSS 颜色](https://tailwindcss.com/docs/customizing-colors) -- [WalletCard 组件](../../05-组件篇/03-钱包组件/WalletCard.md) -- [WalletConfig 组件](../../05-组件篇/03-钱包组件/WalletConfig.md) diff --git "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/03-\350\256\276\350\256\241\345\216\237\345\210\231/index.md" "b/docs/white-book/02-\350\256\276\350\256\241\347\257\207/03-\350\256\276\350\256\241\345\216\237\345\210\231/index.md" deleted file mode 100644 index 259092b32..000000000 --- "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/03-\350\256\276\350\256\241\345\216\237\345\210\231/index.md" +++ /dev/null @@ -1,302 +0,0 @@ -# 第六章:设计原则 - -> 定义设计决策的指导原则 - ---- - -## 6.1 信任优先 - -钱包是用户管理资产的工具,信任是最重要的基础。 - -### 透明 - -**原则**:所有操作都清晰展示细节 - -| 场景 | 透明设计 | -|-----|---------| -| 转账 | 显示完整地址、金额、手续费 | -| 授权 | 显示 DApp 来源、请求权限 | -| 签名 | 显示签名内容(消息/交易) | - -**示例**: - -``` -转账确认 - -发送 0x71C7656EC7ab88b098defB751B7401B5f6d8976F -接收 0xabcd...1234 - -金额 1.5 ETH -手续费 0.002 ETH (~$4.50) -───────────────────────────── -总计 1.502 ETH - -[取消] [确认发送] -``` - -### 可控 - -**原则**:用户始终可以取消、返回、重试 - -| 场景 | 可控设计 | -|-----|---------| -| 多步骤流程 | 每步都可返回上一步 | -| 弹窗操作 | 可点击外部或关闭按钮关闭 | -| 长时操作 | 显示进度,可取消 | - -### 安全提示 - -**原则**:在敏感操作前给予警告 - -**警告级别**: - -| 级别 | 样式 | 场景 | -|-----|------|------| -| 提示 | 蓝色 | 一般信息 | -| 警告 | 黄色 | 需要注意 | -| 危险 | 红色 | 高风险操作 | - -**示例**: - -``` -⚠️ 警告 - -助记词是恢复钱包的唯一方式。 -一旦丢失,资产将无法找回。 - -请确保: -• 周围没有其他人 -• 不要截图保存 -• 用纸笔抄写备份 - -[取消] [我已了解,继续] -``` - ---- - -## 6.2 渐进式复杂度 - -### 新手友好 - -**原则**:核心功能零学习成本 - -**实现方式**: -- 首页直接展示余额和快捷操作 -- 转账流程最少只需 2 步 -- 收款一键展示二维码 - -### 进阶可选 - -**原则**:高级功能不干扰基础体验 - -**实现方式**: -- 质押、DApp 在首页不强调 -- 高级设置放在二级菜单 -- 功能按需加载 - -### 专家高效 - -**原则**:提供快捷操作给熟练用户 - -**实现方式**: -- 地址簿快速选择 -- 最近转账记录 -- 手势操作支持 - ---- - -## 6.3 错误预防 - -### 格式验证 - -**原则**:实时校验输入格式 - -``` -收款地址 -┌───────────────────────────────┐ -│ 0x71C7656EC7ab88b098defB751B │ ✓ 格式正确 -└───────────────────────────────┘ - -收款地址 -┌───────────────────────────────┐ -│ invalid_address │ ✗ -└───────────────────────────────┘ - ⚠️ 地址格式不正确 -``` - -### 二次确认 - -**原则**:大额/异常操作需确认 - -**需要二次确认的场景**: -- 转账金额超过阈值 -- 删除钱包 -- 导出私钥 -- 首次向新地址转账 - -### 可恢复 - -**原则**:提供明确的补救路径 - -| 问题 | 补救路径 | -|-----|---------| -| 忘记图案 | 通过助记词重置 | -| 交易失败 | 显示原因,提供重试 | -| 操作中断 | 保存草稿,下次继续 | - ---- - -## 6.4 一致性体验 - -### 跨链统一 - -**原则**:不同链的操作流程保持一致 - -| 操作 | ETH | TRX | BFMeta | -|-----|-----|-----|--------| -| 转账 | 相同流程 | 相同流程 | 相同流程 | -| 手续费 | "网络费用" | "网络费用" | "网络费用" | -| 地址格式 | 自动识别 | 自动识别 | 自动识别 | - -### 视觉统一 - -**原则**:相同功能使用相同的图标和位置 - -| 功能 | 图标 | 位置 | -|-----|------|------| -| 返回 | ← | 左上角 | -| 更多 | ⋯ | 右上角 | -| 复制 | 📋 | 地址后方 | -| 扫码 | 📷 | 输入框内右侧 | - -### 语言统一 - -**原则**:用通俗语言解释技术概念 - -| 技术术语 | 用户友好表述 | -|---------|-------------| -| Gas Fee | 网络费用 | -| Nonce | (隐藏,不展示) | -| Block Confirmation | 交易确认中 | -| Private Key | 私钥 | -| Mnemonic | 助记词 | - ---- - -## 6.5 自适应布局 - -### 设计目标 - -- **一份代码**:无需为不同设备开发独立版本 -- **内容优先**:根据可用空间展示合适的信息量 -- **交互一致**:核心操作在各设备上保持相同 - -### 布局策略 - -| 设备 | 宽度 | 布局特点 | -|-----|------|---------| -| 小型手机 | < 360px | 紧凑单列,核心信息优先 | -| 标准手机 | 360-414px | 标准单列,完整功能 | -| 大屏手机 | 414-600px | 宽松单列,显示更多细节 | -| 折叠屏 | 600-840px | 卡片并排 | -| 平板 | > 840px | 分栏布局 | - -### 典型场景 - -**钱包卡片** -- 窄屏:垂直排列信息 -- 宽屏:水平展开,显示更多 - -**转账确认** -- 窄屏:全屏弹窗 -- 宽屏:居中卡片式弹窗 - -**设置页面** -- 窄屏:单列列表 -- 宽屏:分组卡片布局 - ---- - -## 6.6 无障碍设计 - -### 色彩对比度 - -- 正文与背景对比度 ≥ 4.5:1 -- 大文字与背景对比度 ≥ 3:1 -- 不仅依赖颜色传达信息 - -### 触控目标 - -- 最小触控区域 44x44px -- 相邻可点击元素间距 ≥ 8px - -### 屏幕阅读器 - -- 所有交互元素有 `aria-label` -- 图标按钮有文字说明 -- 表单字段关联 label - -### 键盘导航 - -- Tab 顺序合理 -- 焦点状态可见 -- 支持 Enter/Space 激活 - ---- - -## 6.7 设计检查清单 - -开发新功能时,检查以下项目: - -### 信任 - -- [ ] 是否清晰展示了所有关键信息? -- [ ] 用户是否可以随时取消/返回? -- [ ] 敏感操作是否有适当警告? - -### 复杂度 - -- [ ] 新手用户能否轻松完成? -- [ ] 高级功能是否不干扰基础体验? -- [ ] 是否提供了快捷操作? - -### 错误预防 - -- [ ] 是否有实时输入验证? -- [ ] 是否有必要的二次确认? -- [ ] 出错后是否有补救路径? - -### 一致性 - -- [ ] 是否与其他类似功能保持一致? -- [ ] 是否使用了正确的图标和位置? -- [ ] 是否使用了用户友好的语言? - -### 响应式 - -- [ ] 是否在不同屏幕尺寸下正常显示? -- [ ] 是否使用了容器查询? -- [ ] 核心功能是否始终可用? - -### 无障碍 - -- [ ] 色彩对比度是否足够? -- [ ] 触控目标是否足够大? -- [ ] 是否支持屏幕阅读器? - ---- - -## 本章小结 - -- 信任优先:透明、可控、安全提示 -- 渐进复杂度:新手友好、进阶可选、专家高效 -- 错误预防:格式验证、二次确认、可恢复 -- 一致性:跨链统一、视觉统一、语言统一 -- 自适应:一份代码适配多种设备 -- 无障碍:色彩、触控、阅读器、键盘 - ---- - -## 下一篇 - -完成设计篇后,继续阅读 [第三篇:架构篇](../../03-架构篇/),了解技术实现方案。 diff --git "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/index.md" "b/docs/white-book/02-\350\256\276\350\256\241\347\257\207/index.md" deleted file mode 100644 index b35dc98e5..000000000 --- "a/docs/white-book/02-\350\256\276\350\256\241\347\257\207/index.md" +++ /dev/null @@ -1,56 +0,0 @@ -# 第二篇:设计篇 - -> 定义产品的「长什么样」 - ---- - -## 概述 - -设计篇回答三个核心问题: - -1. **用户如何操作?** → [交互设计](./01-交互设计/) -2. **界面什么风格?** → [视觉设计](./02-视觉设计/) -3. **设计遵循什么原则?** → [设计原则](./03-设计原则/) - ---- - -## 章节导航 - -### [第四章:交互设计](./01-交互设计/) - -定义信息架构、导航流程和页面结构。 - -**要点**: -- 底部 Tab 导航:首页、历史、设置 -- 栈式导航:原生 App 体验 -- 底部弹窗:选择器、确认框 - -### [第五章:视觉设计](./02-视觉设计/) - -定义视觉系统和品牌风格。 - -**要点**: -- 设计令牌:颜色、字体、间距 -- 组件样式:按钮、卡片、表单 -- 深色模式支持 - -### [第六章:设计原则](./03-设计原则/) - -定义设计决策的指导原则。 - -**要点**: -- 信任优先:透明、可控、安全提示 -- 渐进复杂度:新手友好、进阶可选 -- 响应式设计:适配多种屏幕 - ---- - -## 本篇来源 - -本篇内容来源于 `PDR.md` 的设计原则章节和 `TDD.md` 的 UI 组件规范。 - ---- - -## 下一篇 - -完成设计篇后,继续阅读 [第三篇:架构篇](../03-架构篇/),了解技术实现方案。 diff --git a/docs/white-book/03-UI-Ref/00-Overview.md b/docs/white-book/03-UI-Ref/00-Overview.md new file mode 100644 index 000000000..908a7a324 --- /dev/null +++ b/docs/white-book/03-UI-Ref/00-Overview.md @@ -0,0 +1,18 @@ +# 00. 设计系统概览 (Overview) + +Code: `src/styles/globals.css`, `tailwind.config.js` + +KeyApp 使用了一套基于 **Tailwind CSS 4** 和 **CSS Variables** 的动态主题系统。 + +## 核心原则 + +1. **Semantic Naming**: 不使用具体的颜色名 (e.g., `blue-500`),而是使用语义名 (e.g., `primary`, `destructive`)。 +2. **Dark Mode First**: 设计系统原生支持深色模式。所有颜色 Token 都有对应的 `dark:` 变体(通过 CSS 变量自动处理)。 +3. **Mobile First**: 默认样式针对移动端,使用 `@md:` 等前缀适配桌面端。 + +## 技术栈 + +* **Styling**: Tailwind CSS +* **Component Base**: Radix UI (Headless) +* **Component Lib**: shadcn/ui (Customized) +* **Icons**: Tabler Icons diff --git a/docs/white-book/03-UI-Ref/01-Foundation/01-Colors.md b/docs/white-book/03-UI-Ref/01-Foundation/01-Colors.md new file mode 100644 index 000000000..647c860dc --- /dev/null +++ b/docs/white-book/03-UI-Ref/01-Foundation/01-Colors.md @@ -0,0 +1,31 @@ +# 01. 色彩系统 (Colors) + +基于 HSL 值的 CSS 变量系统。 + +## 基础色板 + +| Token | 描述 | 用途 | +| :--- | :--- | :--- | +| `background` | 页面背景 | body, dialog background | +| `foreground` | 页面文字 | body text | +| `card` | 卡片背景 | Card, Popover | +| `primary` | 主色调 | 主要按钮, 激活状态 | +| `secondary` | 次色调 | 次要按钮, 标签 | +| `muted` | 柔和色 | 辅助文字, 禁用状态 | +| `accent` | 强调色 | 悬停效果, 选中项 | +| `destructive` | 破坏色 | 删除, 错误提示 | + +## 使用示例 + +```tsx +// ✅ 正确:语义化 +
+ + Subtitle +
+ +// ❌ 错误:硬编码 +
+ +
+``` diff --git a/docs/white-book/03-UI-Ref/02-Primitives/Button.md b/docs/white-book/03-UI-Ref/02-Primitives/Button.md new file mode 100644 index 000000000..1c930065a --- /dev/null +++ b/docs/white-book/03-UI-Ref/02-Primitives/Button.md @@ -0,0 +1,31 @@ +# Button 按钮 + +Code: `src/components/ui/button.tsx` + +基础交互组件,支持多种变体和尺寸。 + +## Props + +| Prop | Type | Default | Description | +| :--- | :--- | :--- | :--- | +| `variant` | `default` \| `destructive` \| `outline` \| `secondary` \| `ghost` \| `link` | `default` | 视觉样式变体 | +| `size` | `default` \| `sm` \| `lg` \| `icon` | `default` | 尺寸大小 | +| `asChild` | `boolean` | `false` | 是否作为 Slot 渲染子元素 | + +## Usage + +```tsx +import { Button } from "@/components/ui/button" + +export function Example() { + return ( +
+ + + +
+ ) +} +``` diff --git a/docs/white-book/03-UI-Ref/03-Composites/WalletCard.md b/docs/white-book/03-UI-Ref/03-Composites/WalletCard.md new file mode 100644 index 000000000..de5c6d8fe --- /dev/null +++ b/docs/white-book/03-UI-Ref/03-Composites/WalletCard.md @@ -0,0 +1,27 @@ +# WalletCard 钱包卡片 + +Code: `src/components/wallet/wallet-card.tsx` + +展示钱包概览信息(名称、地址、余额)的核心业务组件。通常用于首页或钱包列表。 + +## Features + +* **自动脱敏**: 自动隐藏/显示地址中间部分。 +* **复制功能**: 点击地址自动复制到剪贴板。 +* **余额展示**: 整合多链余额显示。 +* **视觉反馈**: 支持按下缩放 (Active Scale) 动画。 + +## Usage + +```tsx +import { WalletCard } from "@/components/wallet/wallet-card" + +export function WalletList({ wallet }) { + return ( + openDetail(wallet.id)} + /> + ) +} +``` diff --git a/docs/white-book/03-UI-Ref/README.md b/docs/white-book/03-UI-Ref/README.md new file mode 100644 index 000000000..f6c710105 --- /dev/null +++ b/docs/white-book/03-UI-Ref/README.md @@ -0,0 +1,16 @@ +# 📘 Book T3: The UI Reference (组件技术参考) + +> **Design System & Components** +> +> 本书是 UI 开发者的字典,收录了所有基础组件、业务组件和设计规范。 + +## 📖 目录 + +* [00-Overview.md](./00-Overview.md) - 设计系统概览 +* **01-Foundation (基础)** + * [01-Colors.md](./01-Foundation/01-Colors.md) - 色彩系统 +* **02-Primitives (基础组件)** + * [Button.md](./02-Primitives/Button.md) + * [Sheet.md](./02-Primitives/Sheet.md) +* **03-Composites (业务组件)** + * [WalletCard.md](./03-Composites/WalletCard.md) diff --git "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/01-\346\212\200\346\234\257\351\200\211\345\236\213/index.md" "b/docs/white-book/03-\346\236\266\346\236\204\347\257\207/01-\346\212\200\346\234\257\351\200\211\345\236\213/index.md" deleted file mode 100644 index 6030ec997..000000000 --- "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/01-\346\212\200\346\234\257\351\200\211\345\236\213/index.md" +++ /dev/null @@ -1,290 +0,0 @@ -# 第七章:技术需求 - -> 定义实现本产品所需的技术能力 - ---- - -## 7.1 概述 - -本章定义 BFM Pay 实现所需的**技术能力需求**,而非具体技术选型。任何满足这些需求的技术栈都可用于实现。 - ---- - -## 7.2 运行时环境需求 - -### 目标平台 - -| 平台 | 优先级 | 最低版本 | -|-----|-------|---------| -| iOS WebView | P0 | iOS 14+ | -| Android WebView | P0 | Android 8+ (API 26) | -| 现代浏览器 | P1 | Chrome 90+, Safari 14+, Firefox 90+ | -| DWEB 容器 | P0 | Plaoc 最新版 | - -### 运行时能力 - -| 能力 | 必需性 | 说明 | -|-----|-------|------| -| JavaScript ES2020+ | MUST | async/await, optional chaining, nullish coalescing | -| Web Crypto API | MUST | 加密操作 | -| IndexedDB | MUST | 本地持久化存储 | -| Fetch API | MUST | 网络请求 | -| Web Worker | SHOULD | 后台计算(密钥派生) | -| Service Worker | MAY | 离线支持 | - ---- - -## 7.3 UI 框架需求 - -### 核心能力 - -| 需求 | 说明 | -|-----|------| -| 组件化 | 支持可复用 UI 组件的定义和组合 | -| 响应式渲染 | 状态变化自动触发 UI 更新 | -| 生命周期管理 | 组件挂载/卸载钩子,资源清理 | -| 类型安全 | 编译时类型检查(TypeScript) | - -### 性能指标 - -| 指标 | 目标值 | -|-----|-------| -| 首屏渲染 (FCP) | < 1.5s | -| 可交互时间 (TTI) | < 3s | -| 包体积 (gzip) | < 500KB(不含依赖) | -| 60fps 滚动 | MUST | - ---- - -## 7.4 导航系统需求 - -### 核心能力 - -| 需求 | 说明 | -|-----|------| -| 栈式导航 | 页面以栈结构管理,支持 push/pop | -| 手势返回 | iOS 风格的边缘滑动返回 | -| 转场动画 | 平滑的页面切换动画 | -| 深度链接 | URL 映射到特定页面和状态 | -| 状态保持 | 返回时保持页面滚动位置和表单状态 | - -### 导航模式 - -``` -┌─────────────────────────────────────┐ -│ TabBar │ -│ ┌───────┐ ┌───────┐ ┌───────┐ │ -│ │ Tab A │ │ Tab B │ │ Tab C │ │ -│ └───┬───┘ └───┬───┘ └───┬───┘ │ -│ │ │ │ │ -│ ▼ ▼ ▼ │ -│ Stack A Stack B Stack C │ ← 每个 Tab 独立栈 -│ ┌─────┐ ┌─────┐ ┌─────┐ │ -│ │ A1 │ │ B1 │ │ C1 │ │ -│ │ A2 │ │ │ │ C2 │ │ -│ │ A3 │ │ │ │ │ │ -│ └─────┘ └─────┘ └─────┘ │ -└─────────────────────────────────────┘ -``` - ---- - -## 7.5 状态管理需求 - -### 状态分类 - -| 类型 | 特征 | 生命周期 | -|-----|------|---------| -| UI 状态 | 组件内部,如 hover/focus | 组件生命周期 | -| 表单状态 | 用户输入中的数据 | 表单生命周期 | -| 客户端状态 | 本地设置、钱包数据 | 持久化 | -| 服务端状态 | 从 API 获取的数据 | 需缓存和同步 | - -### 核心能力 - -| 需求 | 说明 | -|-----|------| -| 响应式更新 | 状态变化自动通知订阅者 | -| 选择器 | 订阅状态的特定部分,避免不必要的更新 | -| 持久化 | 关键状态存储到本地 | -| 缓存策略 | 服务端数据缓存、过期、重新验证 | -| 乐观更新 | 立即更新 UI,后台同步服务端 | - -### 服务端状态管理 - -``` -┌─────────────────────────────────────────────────────┐ -│ Cache Layer │ -├─────────────────────────────────────────────────────┤ -│ Query Key │ Data │ State │ StaleTime│ -│ ['balance', addr]│ 100 BFM │ fresh │ 30s │ -│ ['txs', addr] │ [...] │ stale │ 60s │ -│ ['price', 'BFM'] │ $0.15 │ fetching │ 10s │ -└─────────────────────────────────────────────────────┘ - -状态机: - idle → fetching → success/error → stale → refetching → ... -``` - ---- - -## 7.6 表单处理需求 - -### 核心能力 - -| 需求 | 说明 | -|-----|------| -| 受控输入 | 所有输入由状态驱动 | -| 验证 | 同步验证 + 异步验证 | -| 错误处理 | 字段级 + 表单级错误 | -| 提交管理 | 提交中状态、防重复提交 | -| 类型安全 | 表单值的完整类型推断 | - -### 验证能力 - -| 类型 | 时机 | 示例 | -|-----|------|------| -| 同步验证 | onChange/onBlur | 必填、格式、长度 | -| 异步验证 | onBlur/防抖 | 地址格式、余额检查 | -| 表单级验证 | onSubmit | 跨字段校验 | - ---- - -## 7.7 样式系统需求 - -### 核心能力 - -| 需求 | 说明 | -|-----|------| -| 组件化样式 | 样式与组件绑定,避免冲突 | -| 主题系统 | 支持浅色/深色主题切换 | -| 设计令牌 | 颜色、间距、字体等统一变量 | -| 响应式 | 适配不同屏幕尺寸 | -| RTL 支持 | 阿拉伯语等从右到左布局 | - -### 设计令牌结构 - -``` -tokens/ -├── colors/ -│ ├── semantic (primary, destructive, muted...) -│ └── palette (具体色值) -├── spacing/ -│ └── scale (4, 8, 12, 16, 24, 32...) -├── typography/ -│ ├── fontFamily -│ ├── fontSize -│ └── fontWeight -└── radius/ - └── scale (sm, md, lg, full) -``` - ---- - -## 7.8 国际化需求 - -### 核心能力 - -| 需求 | 说明 | -|-----|------| -| 翻译管理 | 分离文本和代码 | -| 插值 | 变量替换:`欢迎,{{name}}` | -| 复数 | 不同数量的文本变体 | -| 日期/数字格式化 | 本地化格式 | -| RTL 布局 | 阿拉伯语、希伯来语支持 | - -### 支持语言 - -| 语言 | 代码 | 优先级 | -|-----|------|-------| -| 简体中文 | zh-CN | P0 | -| 英语 | en | P0 | -| 繁体中文 | zh-TW | P1 | -| 日语 | ja | P2 | -| 韩语 | ko | P2 | - ---- - -## 7.9 安全需求 - -### 加密能力 - -| 能力 | 算法/标准 | -|-----|----------| -| 助记词生成 | BIP39 | -| 密钥派生 | BIP44, SLIP-0010 | -| 签名 | Ed25519, secp256k1 | -| 对称加密 | AES-256-GCM | -| 哈希 | SHA-256, Keccak-256 | -| 密钥派生函数 | PBKDF2, scrypt | - -### 存储安全 - -| 需求 | 说明 | -|-----|------| -| 加密存储 | 敏感数据 AES 加密后存储 | -| 内存保护 | 私钥使用后立即清除 | -| 无日志 | 敏感数据不写入日志 | - ---- - -## 7.10 测试需求 - -### 测试层级 - -| 层级 | 覆盖范围 | 目标覆盖率 | -|-----|---------|----------| -| 单元测试 | 工具函数、服务 | > 80% | -| 组件测试 | UI 组件行为 | > 70% | -| 集成测试 | 模块交互 | 关键路径 | -| E2E 测试 | 用户流程 | 核心流程 | - -### 测试能力需求 - -| 需求 | 说明 | -|-----|------| -| 组件渲染测试 | 验证组件输出 | -| 用户交互模拟 | 点击、输入、滚动 | -| 异步测试 | 等待异步操作完成 | -| Mock | 模拟网络请求和服务 | -| 快照测试 | UI 回归检测 | -| 视觉回归 | 截图对比 | - ---- - -## 7.11 构建和部署需求 - -### 构建产物 - -| 目标 | 说明 | -|-----|------| -| Web SPA | 静态文件,可部署到 CDN | -| DWEB 包 | Plaoc 容器格式 | - -### 构建优化 - -| 需求 | 说明 | -|-----|------| -| 代码分割 | 按路由拆分,首屏最小化 | -| Tree Shaking | 移除未使用代码 | -| 压缩 | JS/CSS 压缩 | -| 资源哈希 | 文件名包含内容哈希,支持长期缓存 | - ---- - -## 本章小结 - -本章定义了实现 BFM Pay 所需的技术能力,包括: -- 运行时环境和平台兼容性 -- UI 框架、导航、状态管理的能力需求 -- 样式系统和国际化需求 -- 安全和加密能力 -- 测试和构建需求 - -具体技术选型可根据团队熟悉度和项目需求决定。 - ---- - -## 下一章 - -继续阅读 [第八章:系统架构](../02-系统架构/)。 diff --git "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/02-\347\263\273\347\273\237\346\236\266\346\236\204/index.md" "b/docs/white-book/03-\346\236\266\346\236\204\347\257\207/02-\347\263\273\347\273\237\346\236\266\346\236\204/index.md" deleted file mode 100644 index 7d3438561..000000000 --- "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/02-\347\263\273\347\273\237\346\236\266\346\236\204/index.md" +++ /dev/null @@ -1,241 +0,0 @@ -# 系统架构 - -> 定义分层架构和模块划分 - ---- - -## 分层架构 - -``` -┌──────────────────────────────────────────────────────────────┐ -│ UI Layer │ -│ 导航系统 + 组件 + 页面 │ -├──────────────────────────────────────────────────────────────┤ -│ State Layer │ -│ 客户端状态 + 服务端状态缓存 │ -├──────────────────────────────────────────────────────────────┤ -│ Service Layer │ -│ 链适配器 + 平台服务 + 工具函数 │ -├──────────────────────────────────────────────────────────────┤ -│ Platform Layer │ -│ Web APIs / DWEB 平台插件 │ -└──────────────────────────────────────────────────────────────┘ -``` - ---- - -## 各层职责规范 - -### UI Layer - -- **MUST** 只负责界面渲染和用户交互 -- **MUST NOT** 包含业务逻辑 -- **SHOULD** 使用声明式组件模式 -- **SHOULD** 支持组件热替换 - -### State Layer - -- **MUST** 区分客户端状态和服务端状态 -- **MUST** 提供响应式状态订阅 -- **SHOULD** 支持状态持久化 -- **SHOULD** 支持细粒度状态订阅(避免过度渲染) - -### Service Layer - -- **MUST** 通过接口抽象隐藏实现细节 -- **MUST** 支持依赖注入模式 -- **MUST NOT** 依赖 UI 层 -- **SHOULD** 提供适配器模式支持多链 - -### Platform Layer - -- **MUST** 统一抽象平台差异 -- **MUST** 提供能力检测机制 -- **SHOULD** 提供降级策略 - ---- - -## 目录结构规范 - -``` -src/ -├── navigation/ # 导航系统 -│ ├── screens/ # 屏幕/页面定义 -│ ├── components/ # 导航组件 -│ ├── hooks/ # 导航 Hooks -│ └── config # 导航配置 -│ -├── pages/ # 页面组件 -│ ├── home/ # 首页 -│ ├── wallet/ # 钱包相关 -│ ├── send/ # 发送 -│ ├── receive/ # 收款 -│ ├── settings/ # 设置 -│ └── authorize/ # DWEB 授权 -│ -├── components/ # 通用组件 -│ ├── ui/ # 基础 UI 组件 -│ ├── common/ # 业务通用组件 -│ ├── wallet/ # 钱包相关组件 -│ └── layout/ # 布局组件 -│ -├── stores/ # 状态管理 -│ ├── wallet # 钱包状态 -│ ├── preferences # 用户偏好 -│ └── ui # UI 状态 -│ -├── services/ # 服务层 -│ ├── adapters/ # 链适配器 -│ ├── platform/ # 平台服务 -│ └── utils/ # 工具函数 -│ -├── hooks/ # 通用 Hooks -├── i18n/ # 国际化 -├── lib/ # 库封装 -│ ├── crypto/ # 加密相关 -│ └── utils/ # 工具函数 -│ -└── styles/ # 全局样式 -``` - ---- - -## 模块依赖规则 - -### 依赖方向 - -``` - Pages/Screens - │ - ▼ - ┌─────────┴─────────┐ - │ │ - ▼ ▼ -Components Stores - │ │ - └─────────┬─────────┘ - │ - ▼ - Services - │ - ▼ - Platform -``` - -### 规范 - -1. **单向依赖** - 上层可依赖下层,下层 MUST NOT 依赖上层 -2. **同层隔离** - 同层模块 SHOULD NOT 相互依赖 -3. **接口依赖** - MUST 依赖抽象接口而非具体实现 -4. **显式依赖** - MUST 通过显式导入声明依赖关系 - ---- - -## 数据流规范 - -### 读取流程 - -``` -用户操作 - │ - ▼ -┌─────────────┐ -│ Component │ ─── 查询状态 ───► 缓存命中? -└─────────────┘ │ - ┌───────┴───────┐ - │ │ - Yes No - │ │ - ▼ ▼ - 返回缓存 调用 Service - │ - ▼ - Chain Adapter - │ - ▼ - 区块链节点 -``` - -### 写入流程 - -``` -用户提交 - │ - ▼ -┌─────────────┐ -│ Component │ ─── 执行变更 ───► Service -└─────────────┘ │ - ▼ - Chain Adapter - │ - ▼ - 签名交易 - │ - ▼ - 广播交易 - │ - ▼ - 更新缓存 - │ - ▼ - UI 更新 -``` - ---- - -## 错误处理规范 - -### 错误分类 - -| 类型 | 处理位置 | 用户反馈 | -|-----|----------|---------| -| 网络错误 | Service 层 | Toast + 重试按钮 | -| 验证错误 | 表单层 | 字段级错误提示 | -| 业务错误 | 页面层 | 错误弹窗 | -| 系统错误 | 全局层 | 错误边界页面 | - -### 规范 - -- **MUST** 在最接近错误源的层级处理错误 -- **MUST** 提供用户可理解的错误消息 -- **MUST** 支持错误重试机制 -- **SHOULD** 实现全局错误边界 -- **SHOULD** 记录错误日志用于调试 - ---- - -## 性能优化规范 - -### 代码分割 - -- **MUST** 按路由进行代码分割 -- **SHOULD** 延迟加载非首屏页面 -- **MAY** 预加载高概率访问的页面 - -### 状态优化 - -- **MUST** 使用细粒度状态订阅 -- **MUST** 避免不必要的状态更新 -- **SHOULD** 使用组件记忆化 - -### 查询优化 - -- **SHOULD** 实现数据预取 -- **SHOULD** 支持并行查询 -- **SHOULD** 合理配置缓存策略 - -### 渲染优化 - -- **MUST** 避免大列表一次性渲染 -- **SHOULD** 使用虚拟滚动 -- **SHOULD** 优化图片加载 - ---- - -## 本章小结 - -- 四层架构:UI → State → Service → Platform -- 单向依赖,层级分明 -- 数据流清晰,读写分离 -- 错误处理分类明确 -- 性能优化策略完备 diff --git "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/03-\345\257\274\350\210\252\347\263\273\347\273\237/index.md" "b/docs/white-book/03-\346\236\266\346\236\204\347\257\207/03-\345\257\274\350\210\252\347\263\273\347\273\237/index.md" deleted file mode 100644 index c33fdea91..000000000 --- "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/03-\345\257\274\350\210\252\347\263\273\347\273\237/index.md" +++ /dev/null @@ -1,290 +0,0 @@ -# 导航系统 - -> 定义栈式导航架构规范 - ---- - -## 导航模式 - -### 栈式导航 - -BFM Pay 采用栈式导航模式,提供原生 App 般的用户体验。 - -``` -┌─────────────────────────────────────┐ -│ Navigation Stack │ -├─────────────────────────────────────┤ -│ [4] 交易详情页 │ ← 栈顶(当前页面) -│ [3] 发送确认页 │ -│ [2] 发送页 │ -│ [1] 钱包详情页 │ -│ [0] 首页 (Tab) │ ← 栈底 -└─────────────────────────────────────┘ -``` - -### 导航类型 - -| 操作 | 说明 | 动画方向 | -|-----|------|---------| -| Push | 压入新页面到栈顶 | 从右进入 | -| Pop | 弹出栈顶页面 | 向右退出 | -| Replace | 替换栈顶页面 | 淡入淡出 | -| Reset | 重置导航栈 | 无动画 | - ---- - -## 页面(Screen)规范 - -### Screen 定义 - -每个 Screen MUST 包含: - -| 属性 | 类型 | 必填 | 说明 | -|-----|------|------|------| -| name | string | 是 | 唯一标识符 | -| path | string | 是 | URL 路径模式 | -| params | ParamSchema | 否 | 参数定义 | -| component | Component | 是 | 渲染组件 | -| options | ScreenOptions | 否 | 页面配置 | - -### Screen 参数 - -``` -ParamSchema = { - [paramName: string]: { - type: 'string' | 'number' | 'boolean' - required: boolean - default?: any - } -} -``` - -### Screen 配置 - -``` -ScreenOptions = { - title: string | null // 页面标题 - headerVisible: boolean // 是否显示头部 - gestureEnabled: boolean // 是否启用手势返回 - animation: AnimationType // 过渡动画类型 -} -``` - ---- - -## Screen 列表 - -### Tab 页面 - -| Screen | 路径 | 说明 | -|--------|------|------| -| MainTabs | / | Tab 容器页 | -| HomeTab | /home | 首页 Tab | -| HistoryTab | /history | 历史 Tab | -| SettingsTab | /settings | 设置 Tab | - -### 钱包相关 - -| Screen | 路径 | 参数 | 说明 | -|--------|------|------|------| -| WalletCreate | /wallet/create | - | 创建钱包 | -| WalletImport | /wallet/import | - | 导入钱包 | -| WalletDetail | /wallet/:id | id: string | 钱包详情 | -| WalletBackup | /wallet/:id/backup | id: string | 备份钱包 | - -### 交易相关 - -| Screen | 路径 | 参数 | 说明 | -|--------|------|------|------| -| Send | /send | token?: string | 发送页 | -| SendConfirm | /send/confirm | tx: TxData | 发送确认 | -| Receive | /receive | - | 收款页 | -| TxDetail | /tx/:hash | hash: string | 交易详情 | - -### 设置相关 - -| Screen | 路径 | 说明 | -|--------|------|------| -| SettingsLanguage | /settings/language | 语言设置 | -| SettingsCurrency | /settings/currency | 货币设置 | -| SettingsChains | /settings/chains | 链配置 | -| SettingsWalletLock | /settings/wallet-lock | 修改钱包锁 | -| SettingsMnemonic | /settings/mnemonic | 查看助记词 | - -### DWEB 授权 - -| Screen | 路径 | 参数 | 说明 | -|--------|------|------|------| -| AuthorizeAddress | /authorize/address/:eventId | eventId: string | 地址授权 | -| AuthorizeSignature | /authorize/signature/:eventId | eventId: string | 签名授权 | - ---- - -## 导航接口规范 - -### INavigator - -``` -INavigator { - // 导航操作 - push(screen: string, params?: object): void - pop(): void - replace(screen: string, params?: object): void - reset(screens: ScreenConfig[]): void - - // 状态查询 - canGoBack(): boolean - getCurrentScreen(): ScreenInfo - getNavigationState(): NavigationState - - // 事件监听 - addListener(event: NavigationEvent, callback: Function): Unsubscribe -} -``` - -### NavigationEvent - -| 事件 | 触发时机 | -|-----|---------| -| beforeNavigate | 导航开始前 | -| afterNavigate | 导航完成后 | -| focus | 页面获得焦点 | -| blur | 页面失去焦点 | - ---- - -## Tab 导航规范 - -### Tab 配置 - -| 属性 | 类型 | 说明 | -|-----|------|------| -| key | string | Tab 唯一标识 | -| label | string | Tab 显示文本 | -| icon | Icon | Tab 图标 | -| screen | string | 对应 Screen | -| badge | number | 角标数字 | - -### Tab 列表 - -| Tab | 图标 | Screen | 说明 | -|-----|------|--------|------| -| home | Wallet | HomeTab | 首页/钱包 | -| history | History | HistoryTab | 交易历史 | -| settings | Settings | SettingsTab | 设置 | - -### Tab 行为规范 - -- **MUST** 点击当前 Tab 滚动到顶部 -- **MUST** 记住每个 Tab 的滚动位置 -- **SHOULD** 支持 Tab 切换动画 -- **MAY** 支持 Tab 长按快捷操作 - ---- - -## URL 同步规范 - -### 路由模式 - -- **MUST** 支持 Hash 路由模式(兼容静态部署) -- **SHOULD** 支持 History 路由模式(可选) - -### URL 格式 - -``` -Hash 模式:https://example.com/#/wallet/abc123 -History 模式:https://example.com/wallet/abc123 -``` - -### 参数编码 - -- **MUST** 路径参数使用 URL 安全字符 -- **MUST** 复杂参数使用 URL Search 编码 -- **SHOULD** 敏感参数加密后传递 - ---- - -## 过渡动画规范 - -### 动画类型 - -| 类型 | 说明 | 使用场景 | -|-----|------|---------| -| slideRight | 从右滑入 | 进入新页面 | -| slideLeft | 从左滑入 | 返回上一页 | -| slideUp | 从下滑入 | 弹出层/Sheet | -| fade | 淡入淡出 | 替换页面 | -| none | 无动画 | 快速切换 | - -### 动画参数 - -| 参数 | 建议值 | 说明 | -|-----|--------|------| -| duration | 250-300ms | 动画时长 | -| easing | ease-out | 缓动函数 | -| delay | 0ms | 延迟时间 | - ---- - -## 手势导航规范 - -### 手势返回 - -- **MUST** 支持从屏幕左边缘向右滑动返回 -- **MUST** 滑动距离 > 屏幕 1/3 时触发返回 -- **SHOULD** 显示返回预览效果 -- **MAY** 支持自定义触发阈值 - -### 禁用场景 - -以下页面 SHOULD 禁用手势返回: - -- 支付确认页(防止误操作) -- 图案锁页面(防止泄露) -- 重要表单页(防止数据丢失) - ---- - -## 深度链接规范 - -### 支持的 Scheme - -| Scheme | 说明 | -|--------|------| -| bfmpay:// | 应用内链接 | -| https://pay.bfm.network | Web 链接 | - -### 链接格式 - -``` -bfmpay://send?to=ADDRESS&amount=100 -bfmpay://wallet/WALLET_ID -bfmpay://authorize/address/EVENT_ID -``` - -### 处理流程 - -``` -接收深度链接 - │ - ▼ -解析 scheme 和 path - │ - ▼ -验证参数合法性 - │ - ├── 无效 ──► 显示错误 - │ - └── 有效 ──► 导航到对应页面 -``` - ---- - -## 本章小结 - -- 栈式导航提供原生 App 体验 -- Screen 统一定义页面和参数 -- Tab 导航管理主要功能入口 -- URL 同步支持页面分享和书签 -- 手势导航增强操作体验 -- 深度链接支持外部唤起 diff --git "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/04-\347\212\266\346\200\201\347\256\241\347\220\206/index.md" "b/docs/white-book/03-\346\236\266\346\236\204\347\257\207/04-\347\212\266\346\200\201\347\256\241\347\220\206/index.md" deleted file mode 100644 index 3257e44e0..000000000 --- "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/04-\347\212\266\346\200\201\347\256\241\347\220\206/index.md" +++ /dev/null @@ -1,331 +0,0 @@ -# 状态管理 - -> 定义应用状态管理架构规范 - ---- - -## 状态分类 - -### 三类状态 - -| 类型 | 特点 | 来源 | 持久化 | 示例 | -|-----|------|------|--------|------| -| 客户端状态 | 本地产生 | 用户操作 | 是 | 钱包列表、偏好设置 | -| 服务端状态 | 远程获取 | API/链 | 缓存 | 余额、交易历史 | -| 临时状态 | 短期存在 | 交互 | 否 | 表单数据、UI 状态 | - -### 状态层次 - -``` -┌──────────────────────────────────────────┐ -│ Temporary State │ -│ (表单级,组件生命周期内) │ -├──────────────────────────────────────────┤ -│ Server State │ -│ (应用级,自动缓存同步) │ -├──────────────────────────────────────────┤ -│ Client State │ -│ (应用级,持久化到本地) │ -└──────────────────────────────────────────┘ -``` - ---- - -## 客户端状态规范 - -### Store 定义 - -每个 Store MUST 包含: - -``` -Store { - // 状态 - state: T - - // 读取 - getState(): T - subscribe(listener: (state: T) => void): Unsubscribe - - // 更新 - setState(updater: (prev: T) => T): void -} -``` - -### 钱包状态 (WalletStore) - -| 字段 | 类型 | 持久化 | 说明 | -|-----|------|--------|------| -| wallets | Wallet[] | 是 | 钱包列表 | -| currentWalletId | string | 是 | 当前钱包 ID | -| selectedChain | ChainId | 是 | 当前选中链 | - -### 偏好状态 (PreferencesStore) - -| 字段 | 类型 | 持久化 | 说明 | -|-----|------|--------|------| -| language | LanguageCode | 是 | 界面语言 | -| currency | CurrencyCode | 是 | 显示货币 | -| theme | 'light' \| 'dark' \| 'system' | 是 | 主题模式 | -| biometricEnabled | boolean | 是 | 生物识别开关 | - -### UI 状态 (UIStore) - -| 字段 | 类型 | 持久化 | 说明 | -|-----|------|--------|------| -| activeSheet | SheetType | 否 | 当前弹窗 | -| toastQueue | Toast[] | 否 | Toast 队列 | -| isLoading | boolean | 否 | 全局加载状态 | - -### 地址簿状态 (AddressBookStore) - -| 字段 | 类型 | 持久化 | 说明 | -|-----|------|--------|------| -| contacts | Contact[] | 是 | 联系人列表 | -| isInitialized | boolean | 否 | 是否已初始化 | - -**Contact 结构:** - -| 字段 | 类型 | 说明 | -|-----|------|------| -| id | string | 唯一 ID | -| name | string | 联系人名称 | -| avatar | string? | 头像(avatar: 协议) | -| addresses | ContactAddress[] | 地址列表(最多 3 个) | -| memo | string? | 私有备注 | -| createdAt | number | 创建时间 | -| updatedAt | number | 更新时间 | - -**ContactAddress 结构:** - -| 字段 | 类型 | 说明 | -|-----|------|------| -| id | string | 唯一 ID | -| address | string | 区块链地址 | -| label | string? | 自定义标签(最多 10 字符) | -| isDefault | boolean? | 是否默认地址 | - ---- - -## 服务端状态规范 - -### 缓存策略 - -| 数据类型 | 缓存时长 | 刷新策略 | -|---------|---------|---------| -| 余额 | 30 秒 | 自动轮询 60s | -| 交易历史 | 1 分钟 | 新交易时刷新 | -| Token 元数据 | 1 小时 | 按需刷新 | -| 汇率 | 5 分钟 | 自动轮询 | -| 链配置 | 24 小时 | 手动刷新 | - -### 查询键规范 - -查询键 MUST 遵循以下命名规则: - -``` -[domain, action, ...params] - -示例: -['wallet', 'balances', address, chainId] -['transaction', 'history', address] -['token', 'metadata', tokenId] -['exchange', 'rates', baseCurrency] -``` - -### 查询状态 - -| 状态 | 说明 | -|-----|------| -| idle | 未开始 | -| loading | 加载中(首次) | -| success | 成功 | -| error | 失败 | -| fetching | 后台刷新中 | - ---- - -## 状态操作规范 - -### Action 定义 - -每个状态域 SHOULD 定义对应的 Actions: - -``` -WalletActions { - addWallet(wallet: Wallet): void - removeWallet(walletId: string): void - updateWallet(walletId: string, updates: Partial): void - setCurrentWallet(walletId: string): void - setSelectedChain(chain: ChainId): void -} -``` - -### Mutation 规范 - -服务端状态变更 MUST 通过 Mutation: - -``` -Mutation { - // 执行变更 - mutate(params: TParams): Promise - - // 状态 - isPending: boolean - isSuccess: boolean - isError: boolean - error: Error | null - - // 回调 - onSuccess?: (result: TResult) => void - onError?: (error: Error) => void - onSettled?: () => void -} -``` - -### 缓存失效 - -变更成功后 MUST 使相关缓存失效: - -| 操作 | 失效的缓存 | -|-----|-----------| -| 转账 | 发送方余额、接收方余额、交易历史 | -| 创建钱包 | 钱包列表 | -| 质押/解质押 | 余额、质押状态 | - ---- - -## 状态持久化规范 - -### 存储键命名 - -``` -bfm_{domain}_{version} - -示例: -bfm_wallets_v1 -bfm_preferences_v1 -bfm_cache_v1 -``` - -### 持久化策略 - -| 状态 | 存储位置 | 加密 | -|-----|---------|------| -| 钱包列表 | 安全存储 | 是 | -| 用户偏好 | 本地存储 | 否 | -| 查询缓存 | 本地存储 | 否 | - -### 版本迁移 - -- **MUST** 在存储数据中包含版本号 -- **MUST** 支持旧版本数据迁移 -- **SHOULD** 在迁移失败时保留原数据 - -``` -迁移流程: -读取存储数据 - │ - ▼ -检查版本号 - │ - ├── 当前版本 ──► 直接使用 - │ - └── 旧版本 ──► 执行迁移 - │ - ├── 成功 ──► 保存新版本 - │ - └── 失败 ──► 使用默认值 + 报告错误 -``` - ---- - -## 状态订阅规范 - -### 细粒度订阅 - -- **MUST** 只订阅组件需要的状态片段 -- **MUST NOT** 订阅整个 Store 状态 -- **SHOULD** 使用选择器(Selector)提取状态 - -### 选择器规范 - -``` -Selector = (state: TState) => TResult - -示例: -// ✓ 好:细粒度选择 -const currentWallet = useSelector(state => state.wallets.find( - w => w.id === state.currentWalletId -)) - -// ✗ 差:订阅全部状态 -const allState = useSelector(state => state) -``` - -### 派生状态 - -派生状态 SHOULD 通过选择器计算: - -| 派生状态 | 依赖 | 计算 | -|---------|------|------| -| 当前钱包 | wallets, currentWalletId | wallets.find(w => w.id === currentWalletId) | -| 当前地址 | currentWallet, selectedChain | currentWallet.addresses[selectedChain] | -| 总资产 | balances, exchangeRates | sum(balances * rates) | - ---- - -## 乐观更新规范 - -### 适用场景 - -| 操作 | 乐观更新 | 原因 | -|-----|---------|------| -| 切换钱包 | 是 | 本地操作,必定成功 | -| 修改昵称 | 是 | 失败概率低 | -| 转账 | 否 | 需等待链上确认 | -| 删除钱包 | 否 | 不可逆操作 | - -### 乐观更新流程 - -``` -用户操作 - │ - ▼ -立即更新 UI(乐观) - │ - ▼ -发送请求 - │ - ├── 成功 ──► 保持状态 - │ - └── 失败 ──► 回滚状态 + 显示错误 -``` - ---- - -## 错误处理规范 - -### 查询错误 - -- **MUST** 显示错误状态 -- **MUST** 提供重试机制 -- **SHOULD** 显示缓存数据(如有) -- **SHOULD** 自动重试网络错误 - -### 变更错误 - -- **MUST** 显示错误消息 -- **MUST** 回滚乐观更新 -- **SHOULD** 保留用户输入 -- **MAY** 提供错误详情查看 - ---- - -## 本章小结 - -- 状态分为客户端、服务端、临时三类 -- 客户端状态需持久化到本地 -- 服务端状态需合理配置缓存 -- 细粒度订阅避免过度渲染 -- 支持乐观更新提升体验 -- 版本迁移确保数据兼容 diff --git "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/05-\347\246\273\347\272\277\350\203\275\345\212\233/index.md" "b/docs/white-book/03-\346\236\266\346\236\204\347\257\207/05-\347\246\273\347\272\277\350\203\275\345\212\233/index.md" deleted file mode 100644 index b7c9701e2..000000000 --- "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/05-\347\246\273\347\272\277\350\203\275\345\212\233/index.md" +++ /dev/null @@ -1,289 +0,0 @@ -# 离线能力篇 - -> 定义应用在无网络环境下的行为规范 - ---- - -## 概述 - -钱包应用 **MUST** 具备离线能力,确保用户在网络不可用时仍能: -- 查看缓存的资产信息 -- 准备交易(离线签名) -- 访问关键设置 - ---- - -## 离线状态检测 - -### 网络状态定义 - -| 状态 | 说明 | 检测方式 | -|-----|------|---------| -| online | 网络正常可用 | navigator.onLine + 心跳检测 | -| offline | 完全无网络 | navigator.onLine = false | -| degraded | 网络不稳定 | 请求超时率 > 50% | -| limited | 部分服务可用 | 特定 API 可达 | - -### 检测规范 - -- **MUST** 监听 `online`/`offline` 事件 -- **MUST** 实现心跳检测(每 30s 检测一次) -- **SHOULD** 区分"无网络"和"服务不可用" -- **SHOULD** 检测网络质量(延迟、丢包) - -### 状态机 - -``` - online - │ - ┌──────┴──────┐ - │ │ - ▼ ▼ - degraded ───► offline - │ │ - └──────┬──────┘ - │ - ▼ - online -``` - ---- - -## 离线数据缓存 - -### 缓存层级 - -| 层级 | 数据类型 | 存储位置 | 有效期 | -|-----|---------|---------|-------| -| L1 | 钱包数据、密钥 | 加密存储 | 永久 | -| L2 | 用户设置、偏好 | 本地存储 | 永久 | -| L3 | 资产余额 | 本地存储 | 缓存+时间戳 | -| L4 | 交易历史 | 本地存储 | 最近 100 条 | -| L5 | 链配置、代币元数据 | 本地存储 | 24 小时 | - -### 缓存规范 - -#### L1 关键数据(永久存储) - -| 数据 | 离线可用 | 说明 | -|-----|---------|------| -| 加密助记词 | ✓ | 可离线签名 | -| 钱包列表 | ✓ | 可查看所有钱包 | -| 地址列表 | ✓ | 可查看/复制地址 | - -#### L3 资产数据(带时间戳缓存) - -``` -CachedBalance { - address: string - chainId: string - balance: string - timestamp: number // 缓存时间 - expiresAt: number // 过期时间 - isStale: boolean // 是否过期 -} -``` - -**显示规范:** -- **MUST** 显示缓存时间("5 分钟前") -- **MUST** 过期数据标记为"可能已过期" -- **SHOULD** 使用不同视觉样式区分新旧数据 - ---- - -## 离线功能矩阵 - -### 功能可用性 - -| 功能 | 在线 | 离线 | 降级模式 | -|-----|------|------|---------| -| 查看钱包列表 | ✓ | ✓ | - | -| 查看地址 | ✓ | ✓ | - | -| 复制地址 | ✓ | ✓ | - | -| 查看余额 | ✓ | 缓存 | 显示缓存时间 | -| 查看交易历史 | ✓ | 缓存 | 显示缓存条数 | -| 发起转账 | ✓ | 准备 | 离线签名,待上线广播 | -| 扫描二维码 | ✓ | ✓ | 解析但不验证 | -| 修改设置 | ✓ | ✓ | 本地保存 | -| 查看助记词 | ✓ | ✓ | 本地解密 | -| 创建钱包 | ✓ | ✓ | 本地生成 | - -### 完全不可用功能 - -| 功能 | 原因 | 离线提示 | -|-----|------|---------| -| 实时余额 | 需要链上查询 | "显示缓存数据" | -| 广播交易 | 需要节点连接 | "交易已签名,恢复网络后发送" | -| 交易确认状态 | 需要链上查询 | "无法获取最新状态" | -| 质押操作 | 需要链上交互 | "需要网络连接" | - ---- - -## 离线签名 - -### 流程规范 - -``` -用户发起转账(离线) - │ - ▼ -验证本地缓存的 nonce - │ - ├── 有缓存 ──► 使用缓存 nonce + 1 - │ - └── 无缓存 ──► 提示"需要网络获取 nonce" - 或允许用户手动输入 - │ - ▼ -构建交易对象(本地) - │ - ▼ -解密私钥,签名交易 - │ - ▼ -存储签名交易到待发送队列 - │ - ▼ -显示"交易已准备,恢复网络后自动发送" -``` - -### 待发送队列 - -``` -PendingTransaction { - id: string - signedTx: string // 签名后的交易 - chainId: string - createdAt: number - expiresAt: number // 过期时间(建议 24h) - status: 'pending' | 'broadcasting' | 'sent' | 'failed' | 'expired' - retryCount: number - error?: string -} -``` - -### 队列管理规范 - -- **MUST** 网络恢复后自动尝试广播 -- **MUST** 显示队列中的待发送交易 -- **MUST** 交易过期后提示用户(nonce 可能已失效) -- **SHOULD** 支持取消待发送交易 -- **SHOULD** 广播失败后自动重试(最多 3 次) - ---- - -## 离线 UI 规范 - -### 全局提示 - -``` -┌─────────────────────────────────────┐ -│ ⚠️ 当前离线 - 部分功能受限 │ -│ [点击查看详情] │ -└─────────────────────────────────────┘ -``` - -**规范要求:** -- **MUST** 离线时显示全局提示条 -- **MUST** 提示条不遮挡核心内容 -- **SHOULD** 提供"查看离线功能"入口 -- **SHOULD** 网络恢复后自动消失 - -### 数据新鲜度指示 - -| 状态 | 视觉表现 | 说明 | -|-----|---------|------| -| 实时 | 正常显示 | < 1 分钟 | -| 较新 | 灰色时间戳 | 1-10 分钟 | -| 过期 | 黄色警告 + 时间戳 | > 10 分钟 | -| 很旧 | 红色警告 | > 1 小时 | - -### 功能禁用状态 - -不可用功能 **MUST** 明确标识: - -``` -┌─────────────────────────────────────┐ -│ [发送] (需要网络) │ -│ └─ 灰色禁用 + 提示文字 │ -└─────────────────────────────────────┘ -``` - ---- - -## 数据同步 - -### 恢复在线后的同步 - -``` -网络恢复 - │ - ├─► 刷新余额(优先级: 高) - │ - ├─► 广播待发送交易(优先级: 高) - │ - ├─► 同步交易历史(优先级: 中) - │ - └─► 更新链配置(优先级: 低) -``` - -### 冲突处理 - -| 冲突类型 | 处理策略 | -|---------|---------| -| 设置冲突 | 本地优先(用户最近修改) | -| 交易 nonce 冲突 | 提示用户重新发起 | -| 余额不一致 | 服务端优先(以链上为准) | - -### 同步状态 - -``` -SyncStatus { - lastSyncTime: number - isSyncing: boolean - pendingChanges: number - syncErrors: SyncError[] -} -``` - ---- - -## 存储管理 - -### 存储配额 - -| 数据类型 | 建议配额 | -|---------|---------| -| 钱包数据 | 无限制 | -| 交易历史 | 最近 100 条/地址 | -| 缓存数据 | 最大 50MB | - -### 清理策略 - -- **SHOULD** 超过配额时清理最旧的缓存 -- **MUST NOT** 自动清理钱包数据 -- **SHOULD** 提供手动清理缓存入口 - ---- - -## 测试要求 - -### 离线测试场景 - -| 场景 | 验证点 | -|-----|-------| -| 启动时离线 | 能正常进入应用,显示缓存 | -| 使用中断网 | 立即显示离线提示 | -| 离线发起转账 | 能完成签名,进入待发送 | -| 恢复网络 | 自动同步,广播待发送 | -| 长时间离线 | 缓存过期正确标识 | - ---- - -## 本章小结 - -- 钱包必须具备离线核心功能 -- 缓存数据需要时间戳和过期标识 -- 离线签名 + 待发送队列支持延迟广播 -- 网络恢复后自动同步和广播 -- UI 需要清晰的离线状态指示 diff --git "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/06-\351\224\231\350\257\257\345\244\204\347\220\206/index.md" "b/docs/white-book/03-\346\236\266\346\236\204\347\257\207/06-\351\224\231\350\257\257\345\244\204\347\220\206/index.md" deleted file mode 100644 index 524db99bc..000000000 --- "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/06-\351\224\231\350\257\257\345\244\204\347\220\206/index.md" +++ /dev/null @@ -1,394 +0,0 @@ -# 错误处理篇 - -> 定义统一的错误处理架构和用户体验规范 - ---- - -## 错误分类体系 - -### 一级分类 - -| 类别 | 前缀 | 说明 | 用户可恢复 | -|-----|------|------|-----------| -| 网络错误 | NET_ | 网络连接问题 | 是(重试) | -| 链错误 | CHAIN_ | 区块链交互问题 | 部分 | -| 验证错误 | VALID_ | 输入验证失败 | 是(修正输入) | -| 认证错误 | AUTH_ | 身份认证问题 | 是(重新认证) | -| 存储错误 | STORE_ | 本地存储问题 | 部分 | -| 系统错误 | SYS_ | 系统级异常 | 否 | - ---- - -## 错误码目录 - -### NET_ 网络错误 - -| 错误码 | 名称 | 用户消息 | 恢复方式 | -|-------|------|---------|---------| -| NET_001 | NetworkOffline | 网络已断开 | 检查网络后重试 | -| NET_002 | RequestTimeout | 请求超时 | 自动重试 | -| NET_003 | ServerUnavailable | 服务暂时不可用 | 稍后重试 | -| NET_004 | RateLimited | 请求过于频繁 | 等待后重试 | -| NET_005 | InvalidResponse | 服务器响应异常 | 重试或联系支持 | - -### CHAIN_ 链错误 - -| 错误码 | 名称 | 用户消息 | 恢复方式 | -|-------|------|---------|---------| -| CHAIN_001 | InsufficientBalance | 余额不足 | 充值后重试 | -| CHAIN_002 | InsufficientGas | Gas 费不足 | 增加 Gas 或充值 | -| CHAIN_003 | NonceConflict | 交易序号冲突 | 重新获取 nonce | -| CHAIN_004 | TransactionRejected | 交易被拒绝 | 检查交易参数 | -| CHAIN_005 | ContractError | 合约执行失败 | 检查合约调用 | -| CHAIN_006 | ChainNotSupported | 不支持此链 | 切换到支持的链 | -| CHAIN_007 | NodeUnavailable | 节点不可用 | 自动切换备用节点 | -| CHAIN_008 | TransactionDropped | 交易已被丢弃 | 重新发起交易 | -| CHAIN_009 | GasPriceTooLow | Gas 价格过低 | 提高 Gas 价格 | -| CHAIN_010 | InvalidAddress | 地址格式无效 | 检查收款地址 | - -### VALID_ 验证错误 - -| 错误码 | 名称 | 用户消息 | 恢复方式 | -|-------|------|---------|---------| -| VALID_001 | RequiredField | 请填写此字段 | 填写必填项 | -| VALID_002 | InvalidFormat | 格式不正确 | 按提示修正 | -| VALID_003 | AmountTooSmall | 金额过小 | 增加金额 | -| VALID_004 | AmountTooLarge | 金额超出限制 | 减少金额 | -| VALID_005 | InvalidMnemonic | 助记词无效 | 检查助记词 | -| VALID_006 | PatternMismatch | 两次图案不一致 | 重新绘制 | -| VALID_007 | PatternTooSimple | 图案连接点不足 | 连接更多点 | -| VALID_008 | InvalidQRCode | 无法识别二维码 | 重新扫描 | - -### AUTH_ 认证错误 - -| 错误码 | 名称 | 用户消息 | 恢复方式 | -|-------|------|---------|---------| -| AUTH_001 | WrongPattern | 图案错误 | 重新绘制 | -| AUTH_002 | BiometricFailed | 生物识别失败 | 使用图案锁 | -| AUTH_003 | BiometricNotAvailable | 生物识别不可用 | 使用图案锁 | -| AUTH_004 | TooManyAttempts | 尝试次数过多 | 等待解锁 | -| AUTH_005 | SessionExpired | 会话已过期 | 重新认证 | -| AUTH_006 | AuthorizationDenied | 授权被拒绝 | 重新请求授权 | - -### STORE_ 存储错误 - -| 错误码 | 名称 | 用户消息 | 恢复方式 | -|-------|------|---------|---------| -| STORE_001 | StorageFull | 存储空间不足 | 清理空间 | -| STORE_002 | DataCorrupted | 数据已损坏 | 重新导入钱包 | -| STORE_003 | DecryptionFailed | 解密失败 | 检查图案 | -| STORE_004 | WalletNotFound | 钱包不存在 | 创建或导入 | -| STORE_005 | MigrationFailed | 数据迁移失败 | 联系支持 | - -### SYS_ 系统错误 - -| 错误码 | 名称 | 用户消息 | 恢复方式 | -|-------|------|---------|---------| -| SYS_001 | UnknownError | 发生未知错误 | 重试或联系支持 | -| SYS_002 | CryptoNotSupported | 加密功能不可用 | 升级浏览器 | -| SYS_003 | OutOfMemory | 内存不足 | 关闭其他应用 | -| SYS_004 | FeatureNotSupported | 功能暂不支持 | 等待更新 | - ---- - -## 错误数据结构 - -### AppError 基础结构 - -``` -AppError { - code: string // 错误码 (如 "CHAIN_001") - name: string // 错误名称 - message: string // 开发者消息(英文) - userMessage: string // 用户消息(多语言 key) - severity: Severity // 严重程度 - recoverable: boolean // 是否可恢复 - retryable: boolean // 是否可重试 - context?: object // 上下文信息 - cause?: Error // 原始错误 - timestamp: number // 发生时间 -} - -Severity = 'info' | 'warning' | 'error' | 'critical' -``` - -### 错误上下文 - -``` -ErrorContext { - // 操作上下文 - operation: string // 操作名称 - chainId?: string // 链 ID - address?: string // 相关地址 - txHash?: string // 交易哈希 - - // 请求上下文 - requestId?: string // 请求 ID - endpoint?: string // API 端点 - - // 设备上下文 - platform: string // 平台 - version: string // 应用版本 -} -``` - ---- - -## 错误处理层级 - -### 处理架构 - -``` -┌─────────────────────────────────────────────────────────┐ -│ UI Layer │ -│ 显示用户友好的错误消息 │ -├─────────────────────────────────────────────────────────┤ -│ Application Layer │ -│ 决定重试/降级/上报策略 │ -├─────────────────────────────────────────────────────────┤ -│ Service Layer │ -│ 转换为 AppError,添加上下文 │ -├─────────────────────────────────────────────────────────┤ -│ Provider Layer │ -│ 捕获原始错误 │ -└─────────────────────────────────────────────────────────┘ -``` - -### 各层职责 - -| 层级 | 职责 | 输出 | -|-----|------|------| -| Provider | 捕获原始异常 | 原始 Error | -| Service | 转换、分类、添加上下文 | AppError | -| Application | 决定处理策略 | 处理决策 | -| UI | 展示给用户 | 用户界面 | - ---- - -## 错误恢复策略 - -### 自动重试 - -``` -RetryConfig { - maxAttempts: number // 最大重试次数 - baseDelay: number // 基础延迟 (ms) - maxDelay: number // 最大延迟 (ms) - backoffFactor: number // 退避系数 - retryableErrors: string[] // 可重试的错误码 -} -``` - -**默认配置:** - -| 参数 | 值 | -|-----|---| -| maxAttempts | 3 | -| baseDelay | 1000ms | -| maxDelay | 30000ms | -| backoffFactor | 2 | - -**可重试错误码:** -- NET_002 (RequestTimeout) -- NET_003 (ServerUnavailable) -- CHAIN_007 (NodeUnavailable) - -### 降级策略 - -| 错误 | 降级方案 | -|-----|---------| -| 主节点不可用 | 切换备用节点 | -| 实时数据不可用 | 显示缓存数据 | -| 生物识别失败 | 回退图案锁认证 | -| WebSocket 断开 | 降级轮询 | - -### 用户干预 - -需要用户干预的情况: - -| 情况 | 用户操作 | -|-----|---------| -| 余额不足 | 充值 | -| 图案错误 | 重新绘制 | -| 授权被拒 | 重新授权 | -| 数据损坏 | 重新导入钱包 | - ---- - -## 错误 UI 规范 - -### Toast 提示(轻量级) - -适用:可自动恢复的临时错误 - -``` -┌─────────────────────────────────────┐ -│ ⚠️ 网络连接不稳定,正在重试... │ -└─────────────────────────────────────┘ -``` - -**规范:** -- 自动消失(3-5 秒) -- 不阻塞用户操作 -- 简短消息 - -### 内联错误(表单级) - -适用:验证错误 - -``` -┌─────────────────────────────────────┐ -│ 收款地址 │ -│ ┌─────────────────────────────────┐ │ -│ │ 0xinvalid... │ │ -│ └─────────────────────────────────┘ │ -│ ❌ 地址格式无效 │ -└─────────────────────────────────────┘ -``` - -**规范:** -- 紧邻出错字段 -- 红色文字 + 图标 -- 具体说明错误原因 - -### 对话框(阻塞级) - -适用:需要用户决策的错误 - -``` -┌─────────────────────────────────────┐ -│ 交易失败 │ -├─────────────────────────────────────┤ -│ │ -│ Gas 价格过低,交易未被确认。 │ -│ │ -│ 建议操作: │ -│ • 提高 Gas 价格重新发送 │ -│ • 取消此交易 │ -│ │ -├─────────────────────────────────────┤ -│ [取消] [重新发送] │ -└─────────────────────────────────────┘ -``` - -**规范:** -- 清晰的错误说明 -- 提供解决方案 -- 明确的操作按钮 - -### 全屏错误页(严重级) - -适用:系统级错误、无法恢复的错误 - -``` -┌─────────────────────────────────────┐ -│ │ -│ 😵 │ -│ │ -│ 出了点问题 │ -│ │ -│ 应用遇到了意外错误。 │ -│ 我们已记录此问题。 │ -│ │ -│ 错误代码: SYS_001 │ -│ │ -│ [重新启动] │ -│ [联系支持] │ -│ │ -└─────────────────────────────────────┘ -``` - ---- - -## 错误日志规范 - -### 日志级别 - -| 级别 | 用途 | 上报 | -|-----|------|------| -| DEBUG | 调试信息 | 否 | -| INFO | 一般信息 | 否 | -| WARN | 警告,已处理 | 可选 | -| ERROR | 错误,需关注 | 是 | -| FATAL | 严重错误 | 立即上报 | - -### 日志结构 - -``` -ErrorLog { - level: LogLevel - error: AppError - context: ErrorContext - stack?: string - userAgent: string - timestamp: number -} -``` - -### 敏感信息过滤 - -**MUST NOT** 记录以下信息: -- 助记词 -- 私钥 -- 图案锁 -- 完整地址(仅记录前 6 + 后 4 位) - ---- - -## 错误上报规范 - -### 上报条件 - -| 错误级别 | 上报 | 时机 | -|---------|------|------| -| info/warning | 否 | - | -| error (可恢复) | 聚合上报 | 每 5 分钟 | -| error (不可恢复) | 即时上报 | 立即 | -| fatal | 即时上报 | 立即 | - -### 上报数据 - -``` -ErrorReport { - errors: ErrorLog[] - sessionId: string - deviceInfo: DeviceInfo - appVersion: string -} -``` - -### 用户隐私 - -- **MUST** 脱敏后再上报 -- **MUST** 允许用户关闭错误上报 -- **SHOULD** 上报前显示上报内容 - ---- - -## 测试要求 - -### 错误场景覆盖 - -| 场景 | 测试点 | -|-----|-------| -| 网络断开 | 显示离线提示 | -| 请求超时 | 自动重试 | -| 余额不足 | 禁用发送 + 提示 | -| 图案错误 | 显示错误动画 + 清空 | -| 节点不可用 | 自动切换 | - -### 错误恢复测试 - -- 重试后成功 -- 降级后可用 -- 用户干预后恢复 - ---- - -## 本章小结 - -- 统一的错误码体系,便于追踪和处理 -- 分层处理:捕获 → 转换 → 决策 → 展示 -- 自动恢复优先,必要时引导用户 -- 错误 UI 分级:Toast → 内联 → 对话框 → 全屏 -- 完善的日志和上报机制 diff --git "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/07-\346\200\247\350\203\275\351\242\204\347\256\227/index.md" "b/docs/white-book/03-\346\236\266\346\236\204\347\257\207/07-\346\200\247\350\203\275\351\242\204\347\256\227/index.md" deleted file mode 100644 index 568605434..000000000 --- "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/07-\346\200\247\350\203\275\351\242\204\347\256\227/index.md" +++ /dev/null @@ -1,282 +0,0 @@ -# 性能预算篇 - -> 定义关键操作的性能指标和优化目标 - ---- - -## 核心性能指标 - -### 加载性能 - -| 指标 | 目标值 | 测量方式 | -|-----|-------|---------| -| 首次内容绘制 (FCP) | < 1.5s | Lighthouse | -| 最大内容绘制 (LCP) | < 2.5s | Lighthouse | -| 可交互时间 (TTI) | < 3.5s | Lighthouse | -| 首字节时间 (TTFB) | < 600ms | Lighthouse | -| 累积布局偏移 (CLS) | < 0.1 | Lighthouse | - -### 运行时性能 - -| 指标 | 目标值 | 说明 | -|-----|-------|------| -| 帧率 (FPS) | 60fps | 滚动/动画时 | -| 输入延迟 | < 100ms | 点击响应 | -| 长任务 | < 50ms | 单个 JS 任务 | -| 内存占用 | < 150MB | 正常使用时 | - ---- - -## 操作性能预算 - -### 导航操作 - -| 操作 | 目标时间 | 说明 | -|-----|---------|------| -| 页面切换动画 | 250-300ms | Stackflow 过渡 | -| Tab 切换 | < 100ms | 无动画情况 | -| 返回上一页 | < 150ms | 含动画 | -| 深度链接打开 | < 1s | 从外部唤起 | - -### 钱包操作 - -| 操作 | 目标时间 | 说明 | -|-----|---------|------| -| 钱包列表加载 | < 100ms | 本地数据 | -| 单钱包余额显示 | < 200ms | 缓存命中 | -| 单钱包余额刷新 | < 2s | 网络请求 | -| 多链余额并行查询 | < 3s | 5 条链 | -| 交易历史加载 | < 1s | 首页(20条) | - -### 加密操作 - -| 操作 | 目标时间 | 说明 | -|-----|---------|------| -| 助记词生成 | < 100ms | BIP39 | -| 地址派生(单链) | < 50ms | BIP44/Ed25519 | -| 地址派生(全链) | < 500ms | 10 条链 | -| 图案验证 | < 500ms | PBKDF2 | -| 交易签名 | < 200ms | 单笔交易 | -| 助记词解密 | < 300ms | AES-GCM | - -### 表单操作 - -| 操作 | 目标时间 | 说明 | -|-----|---------|------| -| 输入响应 | < 16ms | 每次按键 | -| 验证反馈 | < 100ms | 字段级验证 | -| 表单提交 | < 50ms | 本地验证 | -| 地址解析 | < 200ms | ENS/域名解析 | - -### 扫码操作 - -| 操作 | 目标时间 | 说明 | -|-----|---------|------| -| 相机启动 | < 500ms | 首次打开 | -| 二维码识别 | < 100ms | 识别延迟 | -| 结果解析 | < 50ms | URL/地址解析 | - ---- - -## 包体积预算 - -### 总体预算 - -| 资源类型 | 预算 (gzip) | 说明 | -|---------|------------|------| -| 首屏 JS | < 150KB | 关键路径 | -| 首屏 CSS | < 30KB | 关键样式 | -| 总 JS | < 400KB | 所有路由 | -| 总 CSS | < 50KB | 所有样式 | -| 图片/图标 | < 100KB | 首屏资源 | - -### 分包预算 - -| 包 | 预算 (gzip) | 触发条件 | -|---|------------|---------| -| vendor (核心依赖) | < 100KB | 首屏 | -| framework | < 50KB | 首屏 | -| 单路由 chunk | < 30KB | 导航时 | -| 加密库 | < 80KB | 延迟加载 | -| i18n 资源 | < 20KB/语言 | 切换语言 | - -### 依赖预算 - -| 依赖类别 | 预算 | 说明 | -|---------|------|------| -| UI 框架 | < 50KB | React 等 | -| 状态管理 | < 15KB | TanStack 等 | -| 导航 | < 20KB | Stackflow 等 | -| 加密 | < 80KB | noble/scure | -| 工具库 | < 30KB | 总计 | - ---- - -## 内存预算 - -### 常驻内存 - -| 数据 | 预算 | 说明 | -|-----|------|------| -| 应用基础 | < 30MB | 框架+组件 | -| 钱包数据 | < 5MB | 10 个钱包 | -| 缓存数据 | < 20MB | 余额+历史 | -| UI 状态 | < 10MB | 组件状态 | -| 总计 | < 100MB | 正常使用 | - -### 峰值内存 - -| 场景 | 预算 | 说明 | -|-----|------|------| -| 交易签名 | < 150MB | 临时增加 | -| 大列表渲染 | < 130MB | 虚拟滚动 | -| 相机扫码 | < 180MB | 视频流 | - -### 内存泄漏 - -- **MUST** 页面卸载后释放所有订阅 -- **MUST** 组件卸载后清理定时器 -- **MUST** 大对象使用后置 null -- **SHOULD** 定期检查内存增长 - ---- - -## 网络预算 - -### 请求数量 - -| 场景 | 预算 | 说明 | -|-----|------|------| -| 首屏加载 | < 15 | 资源请求 | -| 首页展示 | < 5 | API 请求 | -| 页面切换 | < 3 | 数据请求 | - -### 请求大小 - -| 请求类型 | 预算 | 说明 | -|---------|------|------| -| 余额查询 | < 2KB | 单次响应 | -| 交易历史 | < 10KB | 20 条记录 | -| 链配置 | < 5KB | 全量配置 | - -### 并发请求 - -| 场景 | 最大并发 | 说明 | -|-----|---------|------| -| 多链余额 | 5 | 限流保护 | -| 图片加载 | 3 | 资源加载 | -| API 请求 | 4 | 总并发 | - ---- - -## 动画性能 - -### 动画帧预算 - -| 时间 | 用途 | -|-----|------| -| 16ms | 一帧(60fps) | -| < 10ms | JS 执行 | -| < 4ms | 渲染+合成 | -| < 2ms | 余量 | - -### 动画规范 - -| 动画类型 | 实现方式 | 说明 | -|---------|---------|------| -| 页面切换 | CSS transform | GPU 加速 | -| 列表滚动 | 原生滚动 | 避免 JS 干预 | -| 按钮反馈 | CSS transition | < 100ms | -| 加载动画 | CSS animation | 避免 JS | - -### 禁止的动画模式 - -- **MUST NOT** 动画中修改布局属性(width/height) -- **MUST NOT** 动画中触发重排(offsetTop 等) -- **MUST NOT** 使用 setInterval 做动画 -- **SHOULD NOT** 同时运行超过 3 个动画 - ---- - -## 性能监控 - -### 关键指标采集 - -| 指标 | 采集频率 | 上报 | -|-----|---------|------| -| 页面加载时间 | 每次 | 聚合上报 | -| 接口响应时间 | 每次 | P95/P99 | -| 长任务 | 检测到时 | 即时上报 | -| 内存峰值 | 每分钟 | 超阈值上报 | - -### 性能预警 - -| 指标 | 预警阈值 | 严重阈值 | -|-----|---------|---------| -| FCP | > 2s | > 3s | -| LCP | > 3s | > 5s | -| 接口 P95 | > 3s | > 5s | -| 内存 | > 200MB | > 300MB | - ---- - -## 优化策略 - -### 加载优化 - -| 策略 | 说明 | -|-----|------| -| 代码分割 | 路由级别懒加载 | -| 资源预加载 | prefetch 下一页 | -| 图片优化 | WebP + 响应式 | -| 字体优化 | font-display: swap | - -### 运行时优化 - -| 策略 | 说明 | -|-----|------| -| 虚拟滚动 | 长列表 | -| 防抖节流 | 频繁操作 | -| Web Worker | 密集计算 | -| 请求合并 | 相似请求 | - -### 缓存优化 - -| 策略 | 说明 | -|-----|------| -| 强缓存 | 带 hash 的资源 | -| 协商缓存 | HTML 入口 | -| 本地缓存 | 业务数据 | -| 内存缓存 | 热点数据 | - ---- - -## 性能测试 - -### 测试环境 - -| 设备类型 | 配置 | 说明 | -|---------|------|------| -| 低端设备 | 2GB RAM, 慢 4G | 最低基准 | -| 中端设备 | 4GB RAM, 4G | 主要目标 | -| 高端设备 | 8GB RAM, WiFi | 理想体验 | - -### 测试场景 - -| 场景 | 验证点 | -|-----|-------| -| 冷启动 | FCP < 1.5s | -| 热启动 | TTI < 1s | -| 10 个钱包 | 列表流畅 | -| 100 条交易 | 滚动 60fps | -| 弱网环境 | 超时处理正确 | - ---- - -## 本章小结 - -- 所有关键操作都有明确的性能预算 -- 包体积、内存、网络分别设定预算 -- 动画必须保持 60fps -- 建立性能监控和预警机制 -- 针对低端设备优化 diff --git "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/08-Amount\347\261\273\345\236\213\347\263\273\347\273\237/index.md" "b/docs/white-book/03-\346\236\266\346\236\204\347\257\207/08-Amount\347\261\273\345\236\213\347\263\273\347\273\237/index.md" deleted file mode 100644 index e1fabcc09..000000000 --- "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/08-Amount\347\261\273\345\236\213\347\263\273\347\273\237/index.md" +++ /dev/null @@ -1,430 +0,0 @@ -# Amount 类型系统 - -> 类型安全的货币金额处理 - ---- - -## 概述 - -`Amount` 类是 KeyApp 中处理所有货币金额的核心类型。它提供了类型安全的金额操作,避免了常见的精度问题和单位混淆。 - -### 为什么需要 Amount 类型? - -在加密货币应用中,金额处理面临以下挑战: - -1. **精度问题**: JavaScript 的 `number` 类型无法精确表示大数值 -2. **单位混淆**: 混淆原始单位(如 wei)和格式化单位(如 ETH) -3. **类型不安全**: 字符串表示的金额容易出错 -4. **缺乏元数据**: 金额需要知道其 decimals 和符号才能正确显示 - -`Amount` 类通过封装这些复杂性,提供了统一、安全的金额处理方式。 - ---- - -## 核心设计 - -### 不可变性 - -`Amount` 实例是不可变的。所有算术操作返回新的 `Amount` 实例: - -```typescript -const a = Amount.fromFormatted('1', 18, 'ETH') -const b = Amount.fromFormatted('0.5', 18, 'ETH') -const c = a.add(b) // 返回新实例,a 和 b 不变 -``` - -### 元数据 - -每个 `Amount` 实例携带完整的元数据: - -```typescript -interface AmountData { - _value: Big // 精确的数值(使用 Big.js) - _decimals: number // 小数位数 - _symbol?: string // 货币符号 -} -``` - ---- - -## API 参考 - -### 工厂方法 - -#### `Amount.fromRaw(raw: string | bigint, decimals: number, symbol?: string)` - -从原始值(最小单位)创建 Amount。 - -```typescript -// 1 ETH = 10^18 wei -const eth = Amount.fromRaw('1000000000000000000', 18, 'ETH') -eth.toFormatted() // "1" - -// 100 USDT = 10^8 最小单位 -const usdt = Amount.fromRaw('10000000000', 8, 'USDT') -usdt.toFormatted() // "100" -``` - -#### `Amount.fromFormatted(formatted: string | number, decimals: number, symbol?: string)` - -从格式化值(用户可读)创建 Amount。 - -```typescript -const eth = Amount.fromFormatted('1.5', 18, 'ETH') -eth.toRawString() // "1500000000000000000" - -const usdt = Amount.fromFormatted(100, 8, 'USDT') -usdt.toRawString() // "10000000000" -``` - -#### `Amount.zero(decimals: number, symbol?: string)` - -创建零值 Amount。 - -```typescript -const zero = Amount.zero(18, 'ETH') -zero.isZero() // true -``` - -### 转换方法 - -#### `toRawString(): string` - -返回原始值的字符串表示(无小数点)。 - -```typescript -Amount.fromFormatted('1.5', 18, 'ETH').toRawString() -// "1500000000000000000" -``` - -#### `toRawBigInt(): bigint` - -返回原始值的 bigint 表示。 - -```typescript -Amount.fromFormatted('1', 18, 'ETH').toRawBigInt() -// 1000000000000000000n -``` - -#### `toFormatted(precision?: number): string` - -返回格式化的字符串表示。 - -```typescript -const eth = Amount.fromRaw('1234567890000000000', 18, 'ETH') -eth.toFormatted() // "1.23456789" -eth.toFormatted(2) // "1.23" -eth.toFormatted(8) // "1.23456789" -``` - -#### `toNumber(): number` - -返回 JavaScript number(可能有精度损失)。 - -```typescript -Amount.fromFormatted('1.5', 18, 'ETH').toNumber() // 1.5 -``` - -### 算术操作 - -所有算术操作返回新的 `Amount` 实例。 - -#### `add(other: Amount): Amount` - -加法。 - -```typescript -const a = Amount.fromFormatted('1', 18, 'ETH') -const b = Amount.fromFormatted('0.5', 18, 'ETH') -a.add(b).toFormatted() // "1.5" -``` - -#### `subtract(other: Amount): Amount` - -减法。 - -```typescript -const a = Amount.fromFormatted('1', 18, 'ETH') -const b = Amount.fromFormatted('0.3', 18, 'ETH') -a.subtract(b).toFormatted() // "0.7" -``` - -#### `multiply(factor: number | string): Amount` - -乘法(用于汇率转换等)。 - -```typescript -const eth = Amount.fromFormatted('2', 18, 'ETH') -eth.multiply(1.5).toFormatted() // "3" -``` - -#### `divide(divisor: number | string): Amount` - -除法。 - -```typescript -const eth = Amount.fromFormatted('10', 18, 'ETH') -eth.divide(4).toFormatted() // "2.5" -``` - -### 比较操作 - -#### `isZero(): boolean` - -判断是否为零。 - -#### `isPositive(): boolean` - -判断是否为正数。 - -#### `isNegative(): boolean` - -判断是否为负数。 - -#### `equals(other: Amount): boolean` - -判断是否相等。 - -#### `greaterThan(other: Amount): boolean` - -判断是否大于。 - -#### `lessThan(other: Amount): boolean` - -判断是否小于。 - -#### `greaterThanOrEqual(other: Amount): boolean` - -判断是否大于等于。 - -#### `lessThanOrEqual(other: Amount): boolean` - -判断是否小于等于。 - -### 属性访问 - -```typescript -const eth = Amount.fromFormatted('1.5', 18, 'ETH') -eth.decimals // 18 -eth.symbol // "ETH" -``` - ---- - -## 序列化 - -### JSON 序列化 - -`Amount` 实现了 `toJSON()` 方法,返回可序列化的对象: - -```typescript -const eth = Amount.fromFormatted('1.5', 18, 'ETH') -JSON.stringify(eth) -// {"raw":"1500000000000000000","decimals":18,"symbol":"ETH"} -``` - -### 从 JSON 恢复 - -```typescript -const json = { raw: '1500000000000000000', decimals: 18, symbol: 'ETH' } -const eth = Amount.fromRaw(json.raw, json.decimals, json.symbol) -``` - -### Zod Schema - -用于验证 API 响应: - -```typescript -import { z } from 'zod' -import { Amount } from '@/types/amount' - -export const AmountSchema = z.object({ - raw: z.string(), - decimals: z.number(), - symbol: z.string().optional(), -}).transform(data => Amount.fromRaw(data.raw, data.decimals, data.symbol)) -``` - ---- - -## 使用场景 - -### 资产余额 - -```typescript -interface AssetInfo { - assetType: string - name: string - amount: Amount // 使用 Amount 类型 - decimals: number - logo?: string -} - -// 使用 -const asset: AssetInfo = { - assetType: 'ETH', - name: 'Ethereum', - amount: Amount.fromRaw('1500000000000000000', 18, 'ETH'), - decimals: 18, -} - -// 显示 -console.log(`余额: ${asset.amount.toFormatted()} ${asset.assetType}`) -``` - -### 交易记录 - -```typescript -interface TransactionRecord { - id: string - type: 'send' | 'receive' - amount: Amount // 交易金额 - fee?: Amount // 手续费 - // ... -} -``` - -### 发送交易 - -```typescript -// 用户输入 -const userInput = '1.5' -const amount = Amount.fromFormatted(userInput, 18, 'ETH') - -// 验证余额 -if (amount.greaterThan(balance.amount)) { - throw new Error('余额不足') -} - -// 构建交易(使用原始值) -const rawAmount = amount.toRawBigInt() -``` - -### 费用显示 - -```typescript -const fee = Amount.fromRaw('2000000000000000', 18, 'ETH') -console.log(`手续费: ${fee.toFormatted(6)} ETH`) // "手续费: 0.002 ETH" -``` - ---- - -## 最佳实践 - -### 1. 始终使用工厂方法 - -```typescript -// ✅ 正确 -const amount = Amount.fromFormatted('1.5', 18, 'ETH') - -// ❌ 错误 - 不要直接构造 -const amount = new Amount(...) // Amount 构造函数是私有的 -``` - -### 2. 保持 decimals 一致 - -在同一上下文中操作的 Amount 应该使用相同的 decimals: - -```typescript -// ✅ 正确 -const a = Amount.fromFormatted('1', 18, 'ETH') -const b = Amount.fromFormatted('0.5', 18, 'ETH') -const c = a.add(b) - -// ❌ 危险 - decimals 不匹配 -const a = Amount.fromFormatted('1', 18, 'ETH') -const b = Amount.fromFormatted('100', 6, 'USDT') // 不同 decimals -const c = a.add(b) // 可能导致错误 -``` - -### 3. 使用 toNumber() 谨慎 - -`toNumber()` 可能有精度损失,仅用于显示目的: - -```typescript -// ✅ 用于显示 - - -// ❌ 用于计算 -const result = amount.toNumber() * 2 // 可能丢失精度 -// ✅ 应该使用 -const result = amount.multiply(2) -``` - -### 4. API 边界转换 - -在 API 边界进行 Amount 和原始值的转换: - -```typescript -// 接收 API 响应 -const apiResponse = await fetch('/api/balance') -const data = await apiResponse.json() -const balance = Amount.fromRaw(data.balance, data.decimals, data.symbol) - -// 发送 API 请求 -const amount = Amount.fromFormatted(userInput, 18, 'ETH') -await fetch('/api/transfer', { - body: JSON.stringify({ - amount: amount.toRawString(), - // ... - }) -}) -``` - ---- - -## 迁移指南 - -### 从 string 迁移 - -```typescript -// 旧代码 -interface OldAssetInfo { - amount: string // "1.5" -} - -// 新代码 -interface NewAssetInfo { - amount: Amount -} - -// 迁移 -const oldAsset: OldAssetInfo = { amount: '1.5' } -const newAsset: NewAssetInfo = { - amount: Amount.fromFormatted(oldAsset.amount, 18, 'ETH') -} -``` - -### 从 bigint 迁移 - -```typescript -// 旧代码 -interface OldBalance { - raw: bigint // 1500000000000000000n - formatted: string - decimals: number -} - -// 新代码 -interface NewBalance { - amount: Amount -} - -// 迁移 -const oldBalance: OldBalance = { - raw: 1500000000000000000n, - formatted: '1.5', - decimals: 18 -} -const newBalance: NewBalance = { - amount: Amount.fromRaw(oldBalance.raw.toString(), oldBalance.decimals) -} -``` - ---- - -## 相关文档 - -- [AmountDisplay 组件](../../05-组件篇/02-通用组件/AmountDisplay.md) -- [ITransactionService](../../04-服务篇/02-链服务/ITransactionService.md) -- [IAssetService](../../04-服务篇/02-链服务/IAssetService.md) diff --git "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/index.md" "b/docs/white-book/03-\346\236\266\346\236\204\347\257\207/index.md" deleted file mode 100644 index 177afea94..000000000 --- "a/docs/white-book/03-\346\236\266\346\236\204\347\257\207/index.md" +++ /dev/null @@ -1,76 +0,0 @@ -# 第三篇:架构篇 - -> 定义产品的「怎么构建」 - ---- - -## 概述 - -架构篇回答四个核心问题: - -1. **用什么技术?** → [技术选型](./01-技术选型/) -2. **整体怎么组织?** → [系统架构](./02-系统架构/) -3. **页面怎么跳转?** → [导航系统](./03-导航系统/) -4. **数据怎么管理?** → [状态管理](./04-状态管理/) - ---- - -## 章节导航 - -### [第七章:技术选型](./01-技术选型/) - -定义技术栈及选型理由。 - -**要点**: -- React 19 + Vite 7:现代前端框架 -- TanStack 全家桶:状态管理统一方案 -- shadcn/ui + Tailwind CSS 4:组件与样式 - -### [第八章:系统架构](./02-系统架构/) - -定义分层架构和模块划分。 - -**要点**: -- UI 层、状态层、服务层、平台层 -- 模块化设计,职责清晰 -- 依赖注入,便于测试 - -### [第九章:导航系统](./03-导航系统/) - -定义 Stackflow 导航配置。 - -**要点**: -- 栈式导航,原生体验 -- Activity 模式,参数传递 -- URL 同步,支持 hash 路由 - -### [第十章:状态管理](./04-状态管理/) - -定义 TanStack Store/Query 使用方案。 - -**要点**: -- 本地状态:TanStack Store -- 服务端状态:TanStack Query -- 状态持久化策略 - -### [第十四章:Amount 类型系统](./08-Amount类型系统/) - -定义类型安全的货币金额处理方案。 - -**要点**: -- Amount 不可变类型,携带 decimals 和 symbol 元数据 -- 工厂方法:fromRaw()、fromFormatted()、zero() -- 精确算术操作,避免 JavaScript 浮点数精度问题 -- 序列化/反序列化支持 - ---- - -## 本篇来源 - -本篇内容主要来源于 `TDD.md`(技术设计文档)。 - ---- - -## 下一篇 - -完成架构篇后,继续阅读 [第四篇:服务篇](../04-服务篇/),了解服务层设计。 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/api-providers-status.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/api-providers-status.md" deleted file mode 100644 index 8af9f748a..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/api-providers-status.md" +++ /dev/null @@ -1,59 +0,0 @@ -# API Provider 状态与配置 - -> 记录各链 API Provider 的可用性状态、已知问题和配置建议。 -> -> 最后更新: 2026-01-07 (PR #176) - -## Provider 状态概览 - -| 链 | Provider 类型 | 服务商/URL | 状态 | 余额 | 历史 | 说明 | -|---|---|---|---|---|---|---| -| **ETH** | `blockscout-v1` | `eth.blockscout.com` | ✅ 正常 | ✅ | ✅ | 推荐使用,完全免费且稳定 | -| **ETH** | `ethereum-rpc` | `ethereum-rpc.publicnode.com` | ⚠️ 部分 | ✅ | ❌ | RPC 仅支持余额查询,不支持交易历史 | -| **ETH** | `etherscan-v2` | `api.etherscan.io` | ✅ 正常 | ✅ | ✅ | 需要配置 `ETHERSCAN_API_KEY`(已配置) | -| **ETH** | `ethwallet-v1` | `walletapi.bfmeta.info` | ✅ 正常 | ✅ | ✅ | 自研 API,PR #174 已修复 Schema 问题 | -| **BNB** | `bsc-rpc` | `bsc-rpc.publicnode.com` | ⚠️ 部分 | ✅ | ❌ | RPC 仅支持余额查询 | -| **BSC** | `etherscan-v2` | `api.etherscan.io` | ❌ 需付费 | ❌ | ❌ | BSC 链在 Etherscan V2 中需要付费 Plan | -| **BSC** | `bscwallet-v1` | `walletapi.bfmeta.info` | ⚠️ 部分 | ✅ | ❌ | 交易历史查询返回 `NOTOK`,后端数据问题 | -| **Tron** | `tronwallet-v1` | `walletapi.bfmeta.info` | ✅ 正常 | ✅ | ✅ | PR #174 已修复 Schema 问题 | -| **BTC** | `btcwallet-v1` | `walletapi.bfmeta.info` | ✅ 正常 | ✅ | ✅ | PR #176 修复 Schema 解包问题 | -| **BTC** | `mempool-v1` | `mempool.space` | ✅ 正常 | ✅ | ✅ | 推荐使用的标准 BTC 数据源 | - -## 已知问题与解决方案 - -### 1. BSC WalletAPI 交易历史缺失 -- **现象**: `getTransactionHistory` 返回空数组,API 响应 `{"success":true,"result":{"status":"0","message":"NOTOK","result":[]}}`。 -- **原因**: 即使是活跃地址也无法获取交易,可能是后端 BSC 模块数据同步问题或 BscScan V1 源失效。 -- **建议**: 暂时依赖 RPC 查询余额,交易记录需等待后端修复。 - -### 2. Etherscan V2 鉴权(ETH 链已解决) -- **原现象**: 返回 `Missing/Invalid API Key`。 -- **配置**: 需要在环境变量中设置 `ETHERSCAN_API_KEY`。 -- **状态**: ETH 链已配置 API Key,正常可用。 -- **注意**: BSC 链即使有 Key,普通免费 Plan 也不支持通过统一入口访问,需要使用独立的 BscScan API(V1 已弃用,需迁移到 V2)。 - -### 3. BTC WalletAPI (已修复) -- **原现象**: Schema 解析失败,数据返回空。 -- **修复**: PR #176 添加 `{ success, result }` 包装解包逻辑。 -- **状态**: 正常可用,支持余额查询和交易历史。 - -## 配置建议 - -### 环境变量 -推荐配置以下 API Key 以获得最佳稳定性: - -```bash -# .env.local -ETHERSCAN_API_KEY=your_key_here -TRONGRID_API_KEY=your_key_here -``` - -### 链配置 (default-chains.json) -对于 BTC,确保 Mempool 在前: - -```json -"apis": [ - { "type": "mempool-v1", "endpoint": "https://mempool.space/api" }, - { "type": "btcwallet-v1", "endpoint": "https://walletapi.bfmeta.info/wallet/btc/blockbook" } -] -``` diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/index.md" deleted file mode 100644 index c02fa7324..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/index.md" +++ /dev/null @@ -1,282 +0,0 @@ -# 第十一章:服务架构 - -> 定义应用与区块链交互的架构模式 - ---- - -## 11.1 架构概述 - -BFM Pay 需要支持多条区块链,每条链有不同的协议和 API。服务层的职责是提供**统一的抽象接口**,屏蔽底层差异。 - -### 核心问题 - -| 问题 | 解决方案 | -|-----|---------| -| 链协议差异(EVM vs UTXO vs BFM) | 适配器模式 | -| 能力差异(有的链支持质押,有的不支持) | 可选接口 | -| 运行环境差异(Web vs DWEB) | 平台服务抽象 | -| 类型安全 | 强类型接口定义 | - ---- - -## 11.2 分层架构 - -``` -┌─────────────────────────────────────────────────────────┐ -│ UI Layer │ -│ (页面、组件、用户交互) │ -├─────────────────────────────────────────────────────────┤ -│ Application Layer │ -│ (用例、业务逻辑、状态管理) │ -├─────────────────────────────────────────────────────────┤ -│ Service Layer │ ← 本章重点 -│ (统一接口、适配器注册、服务发现) │ -├─────────────────────────────────────────────────────────┤ -│ Adapter Layer │ -│ (EVM Adapter, BFM Adapter, Bitcoin Adapter...) │ -├─────────────────────────────────────────────────────────┤ -│ Provider Layer │ -│ (RPC Client, REST API, SDK) │ -└─────────────────────────────────────────────────────────┘ -``` - -### 层级职责 - -| 层级 | 职责 | 依赖方向 | -|-----|------|---------| -| UI Layer | 渲染界面、响应用户操作 | 向下 | -| Application Layer | 协调业务逻辑 | 向下 | -| Service Layer | 提供统一接口、管理适配器 | 向下 | -| Adapter Layer | 实现特定链的接口 | 向下 | -| Provider Layer | 与链节点/API 通信 | 外部 | - ---- - -## 11.3 适配器模式 - -### 核心概念 - -``` -┌─────────────────┐ -│ IChainAdapter │ ← 统一接口 -└────────┬────────┘ - │ - ┌────┴────┐ - │ │ - ▼ ▼ -┌───────┐ ┌───────┐ -│ EVM │ │ BFM │ ← 具体实现 -│Adapter│ │Adapter│ -└───────┘ └───────┘ -``` - -### 适配器接口 - -``` -IChainAdapter { - // 链标识 - chainId: string - chainType: 'evm' | 'bfm' | 'utxo' | 'solana' - - // 必需服务 - identity: IIdentityService - asset: IAssetService - transaction: ITransactionService - chain: IChainService - - // 可选服务(不支持时返回 null) - staking: IStakingService | null - nft: INFTService | null - - // 生命周期 - initialize(): Promise - dispose(): void -} -``` - -### 服务可选性 - -适配器 **MUST** 实现所有必需服务,**MAY** 实现可选服务: - -| 服务 | EVM | BFM | Bitcoin | -|-----|-----|-----|---------| -| identity | ✓ | ✓ | ✓ | -| asset | ✓ | ✓ | ✓ | -| transaction | ✓ | ✓ | ✓ | -| chain | ✓ | ✓ | ✓ | -| staking | ✗ | ✓ | ✗ | -| nft | ✓ | ✗ | ✗ | - ---- - -## 11.4 注册中心 - -### 职责 - -- 注册适配器 -- 根据 chainId 查找适配器 -- 管理适配器生命周期 - -### 接口定义 - -``` -IAdapterRegistry { - // 注册适配器工厂 - register(chainType: string, factory: AdapterFactory): void - - // 获取适配器(不存在则创建) - getAdapter(chainId: string): IChainAdapter - - // 检查是否支持 - isSupported(chainId: string): boolean - - // 列出所有支持的链 - listSupported(): ChainInfo[] - - // 清理 - disposeAll(): void -} - -type AdapterFactory = (chainConfig: ChainConfig) => IChainAdapter -``` - -### 使用模式 - -``` -// 伪代码示例 -registry = getAdapterRegistry() - -// 获取 BFM 主网适配器 -adapter = registry.getAdapter('bfm-mainnet') - -// 使用服务 -balance = await adapter.asset.getNativeBalance(address) - -// 检查可选服务 -if (adapter.staking != null) { - stakingInfo = await adapter.staking.getStakingInfo(address) -} -``` - ---- - -## 11.5 服务发现 - -### 能力查询 - -应用层在使用服务前 **SHOULD** 检查能力: - -``` -// 检查链是否支持质押 -function canStake(chainId: string): boolean { - adapter = registry.getAdapter(chainId) - return adapter.staking != null -} - -// 根据能力显示/隐藏 UI -if (canStake(currentChain)) { - showStakingButton() -} -``` - -### 功能降级 - -当可选功能不可用时,应用层 **MUST** 优雅降级: - -| 场景 | 处理方式 | -|-----|---------| -| 质押服务不可用 | 隐藏质押入口 | -| NFT 服务不可用 | 隐藏 NFT 标签页 | -| 网络暂时不可用 | 显示缓存数据 + 提示 | - ---- - -## 11.6 依赖注入 - -### 原则 - -- 服务通过接口引用,不直接依赖具体实现 -- 适配器通过注册中心获取,不硬编码 -- 测试时可替换为 Mock 实现 - -### 依赖图 - -``` -┌─────────────┐ ┌─────────────────┐ -│ TransferUseCase │ ────► │ ITransactionService │ -└─────────────┘ └─────────────────┘ - │ ▲ - │ │ implements - ▼ │ -┌─────────────────┐ ┌─────────────────┐ -│ IAdapterRegistry │ ──► │ EvmTxService │ -└─────────────────┘ └─────────────────┘ -``` - ---- - -## 11.7 错误处理策略 - -### 错误传播 - -``` -Provider Layer - │ RpcError - ▼ -Adapter Layer - │ → 转换为 ChainServiceError - ▼ -Service Layer - │ → 添加上下文信息 - ▼ -Application Layer - │ → 决定重试/展示/上报 - ▼ -UI Layer - │ → 显示用户友好的错误信息 -``` - -### 重试策略 - -| 错误类型 | 策略 | -|---------|-----| -| 网络超时 | 自动重试,最多 3 次,指数退避 | -| 节点限流 | 切换备用节点 | -| 交易 nonce 冲突 | 重新获取 nonce 后重试 | -| 余额不足 | 不重试,提示用户 | - ---- - -## 11.8 缓存策略 - -### 缓存层级 - -| 层级 | 数据 | 有效期 | -|-----|------|-------| -| 内存缓存 | 链信息、代币元数据 | 应用生命周期 | -| 本地存储 | 交易历史、用户设置 | 持久化 | -| 无缓存 | 余额、nonce | 实时查询 | - -### 缓存失效 - -| 事件 | 失效范围 | -|-----|---------| -| 发送交易成功 | 当前地址的余额 | -| 区块确认 | pending 交易状态 | -| 用户切换链 | 当前链的所有缓存 | - ---- - -## 本章小结 - -- 服务层采用适配器模式支持多链 -- 注册中心管理适配器的创建和查找 -- 服务分为必需和可选两类 -- 应用层通过能力查询实现功能降级 -- 错误处理和缓存策略明确定义 - ---- - -## 下一章 - -继续阅读 [第十二章:链服务接口](../02-链服务/)。 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IAssetService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IAssetService.md" deleted file mode 100644 index 61bcfcea9..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IAssetService.md" +++ /dev/null @@ -1,161 +0,0 @@ -# IAssetService 资产服务 - -> 资产余额查询 - ---- - -## 职责 - -查询用户资产余额和代币信息,支持实时订阅。 - ---- - -## 接口定义 - -``` -IAssetService { - // 获取原生代币余额 - getNativeBalance(address: Address): Balance - - // 获取代币余额 - getTokenBalance(address: Address, tokenAddress: Address): Balance - - // 获取所有代币余额 - getTokenBalances(address: Address): Balance[] - - // 获取代币元数据 - getTokenMetadata(tokenAddress: Address): TokenMetadata - - // 订阅余额变化 - subscribeBalance(address: Address, callback: (balance: Balance) => void): Unsubscribe -} -``` - ---- - -## 数据结构 - -### Balance - -``` -Balance { - raw: bigint // 原始值(最小单位) - formatted: string // 格式化值(人类可读) - symbol: string // 代币符号 - decimals: number // 小数位数 -} -``` - -**示例**: - -| raw | decimals | formatted | -|-----|---------|-----------| -| 1000000000000000000 | 18 | "1.0" | -| 50000000 | 8 | "0.5" | - -### TokenMetadata - -``` -TokenMetadata { - address: Address // 合约地址(原生为空) - name: string // 代币全名 - symbol: string // 代币符号 - decimals: number // 小数位数 - logoUri?: string // 图标 URL - standard: TokenStandard -} - -TokenStandard = 'native' | 'ERC20' | 'TRC20' | 'BFM-TOKEN' | 'SPL' -``` - ---- - -## 方法详情 - -### getNativeBalance - -获取原生代币(如 ETH、BFM)余额。 - -**参数**: - -| 参数 | 类型 | 必需 | 说明 | -|-----|------|-----|------| -| address | Address | Y | 查询地址 | - -**返回**:`Balance` - -**行为规范**: -- **MUST** 返回最新链上数据 -- **SHOULD** 支持请求缓存(staleTime: 10s) -- **MUST** 处理地址不存在的情况(返回 0) - ---- - -### getTokenBalances - -批量获取地址持有的所有代币余额。 - -**参数**: - -| 参数 | 类型 | 必需 | 说明 | -|-----|------|-----|------| -| address | Address | Y | 查询地址 | - -**返回**:`Balance[]` - -**行为规范**: -- **SHOULD** 使用批量 RPC 调用优化性能 -- **MAY** 使用索引服务加速查询 -- **MUST** 包含原生代币 - ---- - -### subscribeBalance - -订阅余额实时变化。 - -**参数**: - -| 参数 | 类型 | 必需 | 说明 | -|-----|------|-----|------| -| address | Address | Y | 订阅地址 | -| callback | function | Y | 变化回调 | - -**返回**:`Unsubscribe` - 取消订阅函数 - -**行为规范**: -- **MUST** 立即回调一次当前余额 -- **MUST** 区块更新时检查余额变化 -- **SHOULD** 使用 WebSocket 实时推送 -- **MAY** 使用轮询作为降级方案 - ---- - -## 缓存策略 - -| 数据类型 | 缓存时间 | 失效条件 | -|---------|---------|---------| -| 原生余额 | 10s | 发送交易后 | -| 代币余额 | 30s | 发送交易后 | -| 代币元数据 | 24h | 几乎不变 | - ---- - -## 错误码 - -| 错误码 | 说明 | 处理建议 | -|-------|------|---------| -| ADDRESS_NOT_FOUND | 地址在链上不存在 | 返回余额 0 | -| TOKEN_NOT_FOUND | 代币合约不存在 | 提示用户检查地址 | -| NETWORK_ERROR | 网络请求失败 | 显示缓存数据 + 重试 | -| RATE_LIMIT | 请求频率限制 | 降低请求频率 | - ---- - -## 性能要求 - -| 指标 | 目标值 | -|-----|-------| -| 单次查询延迟 | < 500ms | -| 批量查询延迟 | < 2s(100个代币) | -| 订阅更新延迟 | < 5s(新区块后) | diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IChainService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IChainService.md" deleted file mode 100644 index 8d1d0f4fe..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IChainService.md" +++ /dev/null @@ -1,202 +0,0 @@ -# IChainService 链信息服务 - -> 链状态和网络信息查询 - ---- - -## 职责 - -查询区块链网络的基本信息和运行状态。 - ---- - -## 接口定义 - -``` -IChainService { - // 获取链配置信息 - getChainInfo(): ChainInfo - - // 获取当前区块高度 - getBlockHeight(): bigint - - // 获取 Gas 价格 - getGasPrice(): GasPrice - - // 检查节点健康状态 - healthCheck(): HealthStatus - - // 获取链支持的代币列表 - getTokenList(): TokenInfo[] -} -``` - ---- - -## 数据结构 - -### ChainInfo - -``` -ChainInfo { - chainId: string // 链唯一标识 - name: string // 显示名称 - shortName: string // 简称 - - nativeCurrency: { - name: string // "Ether" - symbol: string // "ETH" - decimals: number // 18 - } - - rpc: { - primary: string // 主 RPC 端点 - fallback: string[] // 备用端点 - } - - explorer: { - name: string // "Etherscan" - url: string // "https://etherscan.io" - txPath: string // "/tx/{hash}" - addressPath: string // "/address/{address}" - } - - blockTime: number // 平均出块时间(秒) - confirmations: number // 建议确认数 - - features: { - eip1559: boolean // 支持 EIP-1559 - staking: boolean // 支持质押 - nft: boolean // 支持 NFT - } -} -``` - -### GasPrice - -``` -GasPrice { - slow: bigint // 慢速价格 - standard: bigint // 标准价格 - fast: bigint // 快速价格 - - // EIP-1559 链额外字段 - baseFee?: bigint - maxPriorityFee?: { - slow: bigint - standard: bigint - fast: bigint - } - - lastUpdated: number // 更新时间戳 -} -``` - -### HealthStatus - -``` -HealthStatus { - isHealthy: boolean - - // 节点状态 - latency: number // 响应延迟(ms) - blockHeight: bigint // 当前区块高度 - peerCount?: number // 节点连接数 - isSyncing: boolean // 是否同步中 - - // 时间信息 - lastUpdated: number - lastBlockTime: number // 最新区块时间 -} -``` - ---- - -## 方法详情 - -### getChainInfo - -获取链的静态配置信息。 - -**行为规范**: -- **SHOULD** 缓存结果(配置不常变) -- **MUST** 包含浏览器链接模板 - -### getBlockHeight - -获取当前最新区块高度。 - -**行为规范**: -- **MUST** 返回最新数据 -- **SHOULD** 缓存 3-5 秒 -- 用于判断交易确认数 - -### getGasPrice - -获取当前 gas 价格建议。 - -**行为规范**: -- **MUST** 区分三个档位 -- **SHOULD** 每 10-15 秒更新 -- **MUST** 处理 EIP-1559 链 - -### healthCheck - -检查节点连接状态。 - -**返回判断逻辑**: - -``` -isHealthy = - latency < 5000ms && - !isSyncing && - (now - lastBlockTime) < 5 * blockTime -``` - ---- - -## 节点健康判断 - -| 指标 | 健康阈值 | 不健康处理 | -|-----|---------|-----------| -| 延迟 | < 5s | 切换备用节点 | -| 同步状态 | 非同步中 | 等待同步完成 | -| 区块滞后 | < 5 个区块时间 | 切换节点 | - ---- - -## 浏览器链接生成 - -``` -// 交易链接 -getTransactionUrl(hash: string): string { - return explorer.url + explorer.txPath.replace('{hash}', hash) -} - -// 地址链接 -getAddressUrl(address: string): string { - return explorer.url + explorer.addressPath.replace('{address}', address) -} -``` - ---- - -## 缓存策略 - -| 数据 | 缓存时间 | 说明 | -|-----|---------|------| -| chainInfo | 永久 | 配置不变 | -| blockHeight | 5s | 频繁变化 | -| gasPrice | 15s | 相对稳定 | -| healthStatus | 30s | 定期检查 | - ---- - -## 错误码 - -| 错误码 | 说明 | -|-------|------| -| CHAIN_NOT_FOUND | 未知的链 ID | -| RPC_ERROR | RPC 调用失败 | -| NODE_SYNCING | 节点同步中 | -| ALL_NODES_DOWN | 所有节点不可用 | diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IIdentityService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IIdentityService.md" deleted file mode 100644 index 55d9bb2a0..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IIdentityService.md" +++ /dev/null @@ -1,156 +0,0 @@ -# IIdentityService 身份服务 - -> 地址派生和消息签名 - ---- - -## 职责 - -管理用户身份,包括从种子派生地址、签名消息、验证签名。 - ---- - -## 接口定义 - -``` -IIdentityService { - // 从种子派生地址 - deriveAddress(seed: bytes, index?: number): Address - - // 批量派生地址 - deriveAddresses(seed: bytes, startIndex: number, count: number): Address[] - - // 验证地址格式 - isValidAddress(address: string): boolean - - // 规范化地址(如 EVM 的 checksum) - normalizeAddress(address: string): Address - - // 签名消息 - signMessage(message: string | bytes, privateKey: bytes): Signature - - // 验证消息签名 - verifyMessage(message: string | bytes, signature: Signature, address: Address): boolean -} -``` - ---- - -## 方法详情 - -### deriveAddress - -从助记词种子派生区块链地址。 - -**参数**: - -| 参数 | 类型 | 必需 | 说明 | -|-----|------|-----|------| -| seed | bytes | Y | 助记词种子(64 bytes) | -| index | number | N | 地址索引,默认 0 | - -**返回**:`Address` - 派生的区块链地址 - -**行为规范**: -- **MUST** 使用 BIP44 标准派生路径 -- **MUST** 返回规范化格式的地址 -- **SHOULD** 缓存派生结果以提高性能 - ---- - -### isValidAddress - -验证地址格式是否正确。 - -**参数**: - -| 参数 | 类型 | 必需 | 说明 | -|-----|------|-----|------| -| address | string | Y | 待验证的地址 | - -**返回**:`boolean` - 是否有效 - -**验证规则**: - -| 链类型 | 规则 | -|-------|------| -| EVM | 0x 开头,40 位十六进制,可选 checksum | -| Bitcoin | 符合 Base58Check 或 Bech32 | -| BFM | 符合 BFM 地址规范 | - ---- - -### signMessage - -使用私钥签名消息。 - -**参数**: - -| 参数 | 类型 | 必需 | 说明 | -|-----|------|-----|------| -| message | string \| bytes | Y | 待签名消息 | -| privateKey | bytes | Y | 私钥 | - -**返回**:`Signature` - 签名结果 - -**行为规范**: -- **MUST** 签名后立即清除私钥内存 -- **MUST** 使用链对应的签名算法 -- **SHOULD** 对消息添加前缀防止跨链重放 - ---- - -## 派生路径规范 - -| 链类型 | 派生路径 | 算法 | 地址格式 | -|-------|---------|------|---------| -| EVM 兼容 | m/44'/60'/0'/0/{index} | secp256k1 | 0x... (checksum) | -| Bitcoin | m/84'/0'/0'/0/{index} | secp256k1 | bc1q... (Native SegWit) | -| Tron | m/44'/195'/0'/0/{index} | secp256k1 | T... (Base58Check) | -| BioForest | m/44'/9999'/0'/0/{index} | Ed25519 | b... (Base58) | - -> **注意**: Bitcoin 使用 BIP84 路径(Native SegWit),不再使用 BIP44 Legacy 路径。 - -### 派生流程 - -``` -助记词 - │ - ▼ BIP39 -种子 (64 bytes) - │ - ▼ BIP32/SLIP-0010 -主密钥 - │ - ▼ 派生路径 -私钥 - │ - ▼ 椭圆曲线 -公钥 - │ - ▼ 哈希 + 编码 -地址 -``` - ---- - -## 错误码 - -| 错误码 | 说明 | 处理建议 | -|-------|------|---------| -| INVALID_SEED | 种子格式无效 | 检查助记词 | -| INVALID_ADDRESS | 地址格式无效 | 提示用户检查 | -| SIGNATURE_FAILED | 签名失败 | 检查私钥 | -| VERIFICATION_FAILED | 签名验证失败 | 签名可能被篡改 | -| UNSUPPORTED_CHAIN | 不支持的链类型 | 检查链配置 | - ---- - -## 安全要求 - -| 要求 | 说明 | -|-----|------| -| 私钥不持久化 | 私钥仅在内存中临时存在 | -| 及时清除 | 使用后立即覆写内存 | -| 不记录日志 | 私钥相关数据不写入日志 | -| 隔离环境 | **SHOULD** 在 Web Worker 中执行 | diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IStakingService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IStakingService.md" deleted file mode 100644 index fcd23f66e..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/IStakingService.md" +++ /dev/null @@ -1,226 +0,0 @@ -# IStakingService 质押服务 - -> 代币质押操作(可选服务) - ---- - -## 职责 - -管理代币质押、解除质押、领取奖励等操作。 - -**注意**:此服务为可选服务,仅支持质押功能的链需要实现。 - ---- - -## 接口定义 - -``` -IStakingService { - // 获取质押信息 - getStakingInfo(address: Address): StakingInfo - - // 获取可用验证者列表 - getValidators(options?: ValidatorOptions): Validator[] - - // 质押 - stake(params: StakeParams): UnsignedTransaction - - // 解除质押 - unstake(params: UnstakeParams): UnsignedTransaction - - // 领取奖励 - claimRewards(address: Address, validator?: Address): UnsignedTransaction - - // 重新委托(更换验证者) - redelegate(params: RedelegateParams): UnsignedTransaction - - // 获取质押 APY - getStakingAPY(): number -} -``` - ---- - -## 数据结构 - -### StakingInfo - -``` -StakingInfo { - // 总览 - totalStaked: bigint // 总质押量 - totalRewards: bigint // 待领取奖励 - - // 委托详情 - delegations: Delegation[] - - // 解绑中 - unbonding: UnbondingEntry[] - - // 汇总 - availableBalance: bigint // 可用余额 - stakingRatio: number // 质押占比 -} - -Delegation { - validator: ValidatorInfo - amount: bigint - rewards: bigint - startTime: number -} - -UnbondingEntry { - validator: ValidatorInfo - amount: bigint - completionTime: number // 完成时间戳 - remainingTime: number // 剩余秒数 -} -``` - -### Validator - -``` -Validator { - address: Address - name: string - description?: string - website?: string - logoUri?: string - - // 状态 - status: 'active' | 'inactive' | 'jailed' - - // 指标 - commission: number // 佣金比例 (0-1) - votingPower: bigint // 投票权重 - delegatorCount: number // 委托人数 - uptime: number // 在线率 (0-1) - - // APY(扣除佣金后) - estimatedAPY: number -} - -ValidatorOptions { - status?: 'active' | 'all' - sortBy?: 'votingPower' | 'commission' | 'apy' - limit?: number -} -``` - -### StakeParams - -``` -StakeParams { - from: Address - validator: Address - amount: bigint -} - -UnstakeParams { - from: Address - validator: Address - amount: bigint -} - -RedelegateParams { - from: Address - srcValidator: Address - dstValidator: Address - amount: bigint -} -``` - ---- - -## 质押流程 - -### 质押 - -``` -1. 用户选择验证者 - │ - ▼ -2. 输入质押金额 - │ - ▼ -3. stake() 构建交易 - │ - ▼ -4. 签名并广播 - │ - ▼ -5. 等待确认 - │ - ▼ -6. 质押生效(下一个 epoch) -``` - -### 解除质押 - -``` -1. 选择要解除的委托 - │ - ▼ -2. 输入解除金额 - │ - ▼ -3. unstake() 构建交易 - │ - ▼ -4. 签名并广播 - │ - ▼ -5. 进入解绑期(通常 7-28 天) - │ - ▼ -6. 解绑完成,资金可用 -``` - ---- - -## 解绑期规范 - -| 链 | 解绑期 | 说明 | -|---|-------|------| -| BFM | 7 天 | | -| Cosmos 系 | 21 天 | | -| Ethereum | 可变 | 取决于退出队列 | - ---- - -## 验证者选择建议 - -向用户展示时 **SHOULD** 排序和过滤: - -``` -推荐验证者 = - status == 'active' && - commission < 10% && - uptime > 99% && - sortBy(estimatedAPY, desc) -``` - ---- - -## 风险提示 - -质押操作 **MUST** 向用户展示以下风险: - -| 风险 | 说明 | -|-----|------| -| 解绑期 | 解除质押需等待 X 天 | -| 惩罚风险 | 验证者作恶可能导致本金损失 | -| 价格风险 | 质押期间代币价格可能波动 | - ---- - -## 错误码 - -| 错误码 | 说明 | -|-------|------| -| VALIDATOR_NOT_FOUND | 验证者不存在 | -| VALIDATOR_INACTIVE | 验证者不活跃 | -| INSUFFICIENT_STAKE | 质押金额不足最小要求 | -| UNBONDING_LIMIT | 解绑数量达到上限 | -| ALREADY_DELEGATED | 已委托给该验证者 | -| REDELEGATE_COOLING | 重新委托冷却中 | diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/ITransactionService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/ITransactionService.md" deleted file mode 100644 index b1402fa78..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/ITransactionService.md" +++ /dev/null @@ -1,234 +0,0 @@ -# ITransactionService 交易服务 - -> 交易构建、签名、广播 - ---- - -## 职责 - -构建、签名、广播交易,查询交易状态和历史。 - ---- - -## 接口定义 - -``` -ITransactionService { - // 估算交易费用 - estimateFee(params: TransferParams): FeeEstimate - - // 构建未签名交易 - buildTransaction(params: TransferParams): UnsignedTransaction - - // 签名交易 - signTransaction(unsignedTx: UnsignedTransaction, privateKey: bytes): SignedTransaction - - // 广播交易 - broadcastTransaction(signedTx: SignedTransaction): TransactionHash - - // 查询交易状态 - getTransactionStatus(hash: TransactionHash): TransactionStatus - - // 查询交易详情 - getTransaction(hash: TransactionHash): Transaction - - // 查询交易历史 - getTransactionHistory(address: Address, options?: PaginationOptions): TransactionPage - - // 订阅交易状态 - subscribeTransaction(hash: TransactionHash, callback: (status: TransactionStatus) => void): Unsubscribe -} -``` - ---- - -## 数据结构 - -### TransferParams - -```typescript -TransferParams { - from: Address // 发送方地址 - to: Address // 接收方地址 - amount: Amount // 发送金额(使用 Amount 类型) - tokenAddress?: Address // 代币地址(原生代币为空) - memo?: string // 备注(部分链支持) - - // 可选的费用覆盖 - gasLimit?: bigint - gasPrice?: bigint - maxFeePerGas?: bigint // EIP-1559 - maxPriorityFeePerGas?: bigint -} -``` - -> 关于 Amount 类型的详细信息,请参阅 [Amount 类型系统](../../03-架构篇/08-Amount类型系统/index.md)。 - -### FeeEstimate - -```typescript -FeeEstimate { - slow: Fee // 慢速(~5分钟) - standard: Fee // 标准(~1分钟) - fast: Fee // 快速(~15秒) -} - -Fee { - amount: Amount // 费用金额(使用 Amount 类型) - estimatedTime: number // 预估确认时间(秒) - gasLimit: bigint - gasPrice: Amount -} -``` - -### TransactionStatus - -``` -TransactionStatus { - status: 'pending' | 'confirming' | 'confirmed' | 'failed' - confirmations: number - requiredConfirmations: number - error?: string // 失败原因 -} -``` - -### Transaction - -```typescript -Transaction { - hash: TransactionHash - from: Address - to: Address - amount: Amount // 交易金额(使用 Amount 类型) - fee: Amount // 手续费(使用 Amount 类型) - status: TransactionStatus - timestamp: number - blockNumber?: bigint - memo?: string - type: TransactionType -} - -TransactionType = 'transfer' | 'token-transfer' | 'contract-call' | 'stake' | 'unstake' -``` - ---- - -## 交易流程 - -### 状态机 - -``` - 构建交易 - │ - ▼ - ┌───────────────┐ - │ Unsigned │ ← 待签名 - └───────┬───────┘ - │ 签名 - ▼ - ┌───────────────┐ - │ Signed │ ← 待广播 - └───────┬───────┘ - │ 广播 - ▼ - ┌───────────────┐ - │ Pending │ ← 等待打包 - └───────┬───────┘ - │ - ┌───────┴───────┐ - ▼ ▼ - ┌─────────┐ ┌─────────┐ - │Confirming│ │ Failed │ - └────┬────┘ └─────────┘ - │ 达到确认数 - ▼ - ┌─────────┐ - │Confirmed│ - └─────────┘ -``` - -### 完整流程 - -``` -1. 用户输入转账参数 - │ - ▼ -2. estimateFee() 获取费用选项 - │ - ▼ -3. 用户确认费用 - │ - ▼ -4. buildTransaction() 构建交易 - │ - ▼ -5. 用户绘制图案解锁私钥 - │ - ▼ -6. signTransaction() 签名 - │ - ▼ -7. broadcastTransaction() 广播 - │ - ▼ -8. subscribeTransaction() 等待确认 - │ - ▼ -9. 显示结果 -``` - ---- - -## 方法详情 - -### estimateFee - -估算交易费用,返回三个档位供用户选择。 - -**行为规范**: -- **MUST** 基于当前网络状态计算 -- **MUST** 考虑代币转账的额外 gas -- **SHOULD** 留 20% 余量避免失败 - -### buildTransaction - -构建未签名交易。 - -**行为规范**: -- **MUST** 自动获取 nonce -- **MUST** 验证余额充足 -- **SHOULD** 支持自定义 gas 设置 - -### broadcastTransaction - -将签名交易广播到网络。 - -**行为规范**: -- **MUST** 返回交易哈希 -- **SHOULD** 检测交易是否已存在 -- **MUST** 处理节点拒绝的情况 - ---- - -## 错误码 - -| 错误码 | 说明 | 用户提示 | -|-------|------|---------| -| INSUFFICIENT_BALANCE | 余额不足 | "余额不足,请确保有足够的 {symbol}" | -| INSUFFICIENT_FEE | 手续费不足 | "需要至少 {amount} {symbol} 作为手续费" | -| INVALID_RECIPIENT | 收款地址无效 | "请输入正确的收款地址" | -| NONCE_TOO_LOW | Nonce 冲突 | "交易冲突,请稍后重试" | -| TRANSACTION_REJECTED | 交易被拒绝 | "交易被网络拒绝:{reason}" | -| TRANSACTION_TIMEOUT | 交易超时 | "交易长时间未确认,请检查网络" | -| GAS_TOO_LOW | Gas 不足 | "手续费设置过低,交易可能失败" | - ---- - -## 确认数要求 - -| 链 | 建议确认数 | 原因 | -|---|----------|------| -| Ethereum | 12 | 区块重组可能性 | -| BSC | 15 | 出块快,需更多确认 | -| Bitcoin | 6 | 行业标准 | -| BFM | 1 | 共识机制保证 | diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/index.md" deleted file mode 100644 index acae4a89e..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/index.md" +++ /dev/null @@ -1,80 +0,0 @@ -# 链服务 - -> 定义与区块链交互的标准接口规范 - ---- - -## 概述 - -链服务层提供与区块链网络交互的统一抽象接口,屏蔽不同链的协议差异。 - ---- - -## 服务列表 - -| 服务 | 说明 | 必需性 | -|-----|------|-------| -| [IIdentityService](./IIdentityService) | 地址派生、消息签名 | MUST | -| [IAssetService](./IAssetService) | 资产余额查询 | MUST | -| [ITransactionService](./ITransactionService) | 交易构建、签名、广播 | MUST | -| [IChainService](./IChainService) | 链信息查询 | MUST | -| [IStakingService](./IStakingService) | 质押操作 | MAY | -| INFTService | NFT 资产查询 | MAY | - -## 实现规范 - -| 文档 | 说明 | -|-----|------| -| [实现规范](./实现规范/) | 各链适配器的具体实现要求(BioForest/EVM/Tron/Bitcoin) | - ---- - -## 接口设计原则 - -1. **异步优先**:所有涉及网络/计算的方法 **MUST** 返回 Promise -2. **错误明确**:失败时 **MUST** 抛出带有错误码的异常 -3. **可取消**:长时间操作 **SHOULD** 支持 AbortSignal -4. **类型安全**:参数和返回值 **MUST** 有明确类型定义 - ---- - -## 事件订阅规范 - -所有 `subscribe*` 方法 **MUST** 遵循: - -1. 返回 `Unsubscribe` 函数用于取消订阅 -2. 立即调用一次 callback 返回当前状态 -3. 状态变化时调用 callback -4. 网络错误时通过 callback 第二个参数传递错误 - -### 重连策略 - -| 场景 | 策略 | -|-----|------| -| 网络断开 | 指数退避重连(1s, 2s, 4s, 8s, max 30s) | -| 服务端错误 | 延迟 5s 后重试 | -| 认证失败 | 不重试,通知上层 | - ---- - -## 错误处理规范 - -### 错误结构 - -``` -ChainServiceError { - code: string // 机器可读错误码 - message: string // 人类可读错误信息 - details?: object // 额外错误详情 - cause?: Error // 原始错误 -} -``` - -### 错误分类 - -| 类别 | 前缀 | 处理策略 | -|-----|------|---------| -| 网络错误 | NETWORK_ | 可重试 | -| 验证错误 | VALIDATION_ | 不可重试,需用户修改输入 | -| 业务错误 | BUSINESS_ | 根据具体情况 | -| 系统错误 | SYSTEM_ | 记录日志,提示用户联系支持 | diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/\345\256\236\347\216\260\350\247\204\350\214\203/02-web3-adapters.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/\345\256\236\347\216\260\350\247\204\350\214\203/02-web3-adapters.md" deleted file mode 100644 index 1b699bba3..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/\345\256\236\347\216\260\350\247\204\350\214\203/02-web3-adapters.md" +++ /dev/null @@ -1,209 +0,0 @@ -# Web3 链适配器实现 - -> 本文档描述 EVM、Tron、Bitcoin 链适配器的实现细节。 - -## 概述 - -Web3 适配器为非 Bioforest 链提供统一接口,包括: - -| 链类型 | 适配器 | API 端点 | 签名状态 | -|-------|--------|---------|---------| -| EVM (ETH/BSC) | `EvmAdapter` | PublicNode JSON-RPC | ✅ 完整 | -| Tron | `TronAdapter` | PublicNode HTTP API | ✅ 完整 | -| Bitcoin | `BitcoinAdapter` | mempool.space REST | ⚠️ 部分 | - -## 目录结构 - -``` -src/services/chain-adapter/ -├── evm/ -│ ├── adapter.ts # 主适配器 -│ ├── identity-service.ts # 地址验证 -│ ├── asset-service.ts # 余额查询 -│ ├── chain-service.ts # 链信息、Gas 价格 -│ ├── transaction-service.ts # 交易构建、签名、广播 -│ └── types.ts -├── tron/ -│ ├── adapter.ts -│ ├── identity-service.ts # Base58Check 地址 -│ ├── asset-service.ts # TRX 余额 -│ ├── chain-service.ts # 带宽/能量 -│ ├── transaction-service.ts -│ └── types.ts -├── bitcoin/ -│ ├── adapter.ts -│ ├── identity-service.ts # Bech32/Base58 地址 -│ ├── asset-service.ts # UTXO 余额 -│ ├── chain-service.ts # 费率估算 -│ ├── transaction-service.ts # UTXO 选择 -│ └── types.ts -└── index.ts # 适配器注册 -``` - -## EVM 适配器 - -### 地址派生 - -使用 BIP44 路径 `m/44'/60'/0'/0/index`: - -```typescript -import { HDKey } from '@scure/bip32' -import { keccak_256 } from '@noble/hashes/sha3.js' - -async deriveAddress(seed: Uint8Array, index = 0): Promise
{ - const hdKey = HDKey.fromMasterSeed(seed) - const derived = hdKey.derive(`m/44'/60'/0'/0/${index}`) - const pubKey = secp256k1.getPublicKey(derived.privateKey!, false) - const hash = keccak_256(pubKey.slice(1)) - return '0x' + bytesToHex(hash.slice(-20)) -} -``` - -### 交易签名 - -使用 RLP 编码和 EIP-155 签名: - -```typescript -// 1. 构建交易数据 -const txData = { - nonce: await this.rpc('eth_getTransactionCount', [from, 'pending']), - gasPrice: await this.rpc('eth_gasPrice'), - gasLimit: '0x5208', // 21000 for simple transfer - to, value, data: '0x', - chainId: this.evmChainId, -} - -// 2. RLP 编码(EIP-155 预签名) -const rawTx = this.rlpEncode([nonce, gasPrice, gasLimit, to, value, data, chainId, '0x', '0x']) - -// 3. 签名 -const msgHash = keccak_256(hexToBytes(rawTx.slice(2))) -const sig = secp256k1.sign(msgHash, privateKey, { format: 'recovered' }) -const v = chainId * 2 + 35 + sig.recovery - -// 4. 编码签名交易 -const signedRaw = this.rlpEncode([nonce, gasPrice, gasLimit, to, value, data, v, r, s]) -``` - -### API 调用 - -使用标准 Ethereum JSON-RPC: - -```typescript -private async rpc(method: string, params: unknown[] = []): Promise { - const response = await fetch(this.rpcUrl, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params }), - }) - const json = await response.json() - return json.result -} -``` - -## Tron 适配器 - -### 地址格式 - -Tron 使用 Base58Check 编码,前缀 `0x41`: - -```typescript -// 公钥 → 地址 -const pubKeyHash = keccak_256(pubKey.slice(1)).slice(-20) -const payload = new Uint8Array([0x41, ...pubKeyHash]) -const checksum = sha256(sha256(payload)).slice(0, 4) -return base58.encode([...payload, ...checksum]) // 以 'T' 开头 -``` - -### 交易流程 - -1. **创建交易**:调用 `/wallet/createtransaction` -2. **签名**:对 `txID`(已是 hash)进行 secp256k1 签名 -3. **广播**:调用 `/wallet/broadcasttransaction` - -```typescript -// 创建交易 -const rawTx = await this.api('/wallet/createtransaction', { - owner_address: hexAddress, - to_address: toHexAddress, - amount: Number(amount.raw), -}) - -// 签名(Tron 的 txID 已经是 hash) -const sig = secp256k1.sign(hexToBytes(rawTx.txID), privateKey, { format: 'recovered' }) - -// 广播 -await this.api('/wallet/broadcasttransaction', { ...rawTx, signature: [bytesToHex(sig)] }) -``` - -## Bitcoin 适配器 - -### 地址类型支持 - -| 类型 | 前缀 | BIP 路径 | 编码 | -|-----|------|---------|------| -| P2WPKH (SegWit) | bc1q | m/84'/0'/0'/0/x | Bech32 | -| P2TR (Taproot) | bc1p | m/86'/0'/0'/0/x | Bech32m | -| P2PKH (Legacy) | 1 | m/44'/0'/0'/0/x | Base58Check | - -默认使用 P2WPKH (Native SegWit): - -```typescript -import { bech32 } from '@scure/base' - -async deriveAddress(seed: Uint8Array, index = 0): Promise
{ - const hdKey = HDKey.fromMasterSeed(seed) - const derived = hdKey.derive(`m/84'/0'/0'/0/${index}`) - const pubKeyHash = ripemd160(sha256(derived.publicKey!)) - const words = bech32.toWords(pubKeyHash) - return bech32.encode('bc', [0, ...words]) -} -``` - -### UTXO 查询 - -使用 mempool.space API: - -```typescript -// 获取 UTXO 列表 -const utxos = await this.api(`/address/${address}/utxo`) - -// 计算余额 -const info = await this.api(`/address/${address}`) -const balance = info.chain_stats.funded_txo_sum - info.chain_stats.spent_txo_sum -``` - -### 交易签名限制 - -Bitcoin 交易签名比 EVM/Tron 复杂,需要: - -1. UTXO 选择算法 -2. 为每个输入构建 sighash -3. 签名每个输入 -4. 构建完整的 witness 数据 - -**当前状态**:余额查询、UTXO 列表、交易历史已实现。完整签名需要集成 `bitcoinjs-lib` 或类似库。 - -## 依赖库 - -| 库 | 用途 | -|---|------| -| `@noble/curves` | secp256k1 签名 | -| `@noble/hashes` | sha256, keccak256, ripemd160 | -| `@scure/bip32` | HD 密钥派生 | -| `@scure/bip39` | 助记词处理 | -| `@scure/base` | bech32, base58 编码 | -| `viem` | EVM 工具(可选,用于进一步简化)| - -## 测试 - -```bash -# 运行适配器测试 -pnpm test -- --testPathPattern="chain-adapter" -``` - -## 相关文档 - -- [链配置管理](../../07-链配置管理/index.md) -- [测试网络](../../../08-测试篇/06-测试网络/index.md) -- [ITransactionService](../ITransactionService.md) diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/\345\256\236\347\216\260\350\247\204\350\214\203/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/\345\256\236\347\216\260\350\247\204\350\214\203/index.md" deleted file mode 100644 index 6831e2995..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/02-\351\223\276\346\234\215\345\212\241/\345\256\236\347\216\260\350\247\204\350\214\203/index.md" +++ /dev/null @@ -1,424 +0,0 @@ -# 链服务实现规范 - -> 各链适配器的具体实现要求 - ---- - -## 概述 - -本文档定义各链适配器的实现规范,补充接口定义中未涵盖的链特有逻辑。 - ---- - -## 架构层次 - -``` -┌─────────────────────────────────────────────────────────┐ -│ chain-adapter │ -│ (统一的链适配器接口) │ -└────────────────────────┬────────────────────────────────┘ - │ - ┌───────────────┴───────────────┐ - │ │ - ▼ ▼ -┌─────────────────────┐ ┌─────────────────────┐ -│ 内部依赖 (SDK) │ │ 外部依赖 (API) │ -│ services/*-sdk/ │ │ apis/bnqkl_wallet/ │ -├─────────────────────┤ ├─────────────────────┤ -│ • 密钥派生 │ │ • 余额查询 │ -│ • 交易构建 │ │ • 区块信息 │ -│ • 交易签名 │ │ • 交易广播 │ -│ • 密码验证 │ │ • 交易历史 │ -│ (本地计算) │ │ (网络请求) │ -└─────────────────────┘ └─────────────────────┘ -``` - -**内部依赖** (`services/bioforest-sdk/`): -- 链核心逻辑的本地实现 -- 不可替换,是 chain-adapter 的底层支柱 -- **必须自主完备**:对外暴露完整 API,内部封装外部依赖 -- 其他 service 只能依赖内部 SDK,不能直接依赖外部 API - -**外部依赖** (`apis/bnqkl_wallet/`): -- 第三方 API 服务的封装 -- 可替换(如切换 RPC 节点) -- **只被内部 SDK 使用**,不被其他 service 直接依赖 - ---- - -## 适配器架构 - -### 基础接口 - -```typescript -interface IChainAdapter { - readonly chainId: string - readonly chainType: ChainConfigType // 'bioforest' | 'evm' | 'bip39' | 'custom' - - // 核心服务 - readonly identity: IIdentityService - readonly asset: IAssetService - readonly transaction: ITransactionService - readonly chain: IChainService - - // 可选服务 - readonly staking: IStakingService | null - - // 生命周期 - initialize(config: ChainConfig): Promise - dispose(): void -} -``` - -### 适配器注册 - -```typescript -interface IAdapterRegistry { - register(type: ChainConfigType, factory: AdapterFactory): void - getAdapter(chainId: string): IChainAdapter - hasAdapter(chainId: string): boolean -} - -type AdapterFactory = (config: ChainConfig) => IChainAdapter -``` - ---- - -## BioForest 链 - -适用于:BFMeta, CCChain, PMChain, BFChainV2, BTGMeta, BIWMeta, ETHMeta, Malibu - -### 密钥派生 - -| 项目 | 规范 | -|-----|------| -| 算法 | Ed25519 | -| 种子生成 | SHA256(secret) → 32 bytes | -| 地址格式 | {prefix} + Base58Check(RIPEMD160(SHA256(publicKey))) | -| 默认前缀 | `b` | - -### 交易类型 - -| 类型 | 用途 | 手续费基准 | -|-----|------|-----------| -| 转账 | 原生代币/资产转移 | 动态(链上查询) | -| 设置交易密码 | 启用二次签名 | 固定 | -| 资产销毁 | 销毁代币 | 动态 | - -### 交易密码(二次签名) - -BioForest 链支持可选的交易密码机制: - -``` -交易创建流程: -1. 检查地址是否启用交易密码 -2. 如已启用,签名时需提供 paySecret -3. 交易体包含 signSignature 字段 -``` - -### API 端点 - -| 接口 | 路径 | 说明 | -|-----|------|------| -| 余额查询 | `/wallet/{chain}/address/balance` | 查询地址余额 | -| 地址信息 | `/wallet/{chain}/address/info` | 查询地址详情(含二次签名状态) | -| 交易广播 | `/wallet/{chain}/transactions/broadcast` | 广播签名交易 | -| 交易查询 | `/wallet/{chain}/transactions/query` | 查询历史交易 | -| 手续费查询 | `/wallet/{chain}/blockAveFee` | 获取建议手续费 | - -### 特殊处理 - -- **MUST** 检查地址是否冻结(isFreezed) -- **MUST** 处理交易密码验证 -- **SHOULD** 缓存区块高度用于交易构建 - -### SDK 集成 - -BioForest 链使用 `@bfchain/core` SDK 进行交易创建和签名。SDK 采用源代码集成 + 延迟加载架构。 - -#### 目录结构 - -```typescript -src/services/bioforest-sdk/ -├── index.ts // SDK 高层 API(公开接口) -├── types.ts // 类型定义 -├── genesis-block.json // 创世块配置(~544KB,延迟加载) -└── internal/ // SDK 核心实现(从 @btgmeta-bundle 迁移) - ├── index.ts // setup() 入口函数 - ├── core/ // 核心类 - │ ├── @type.ts - │ ├── const.ts - │ └── core.ts // BTGMetaBundleCore 类 - └── transaction/ // 交易控制器 - ├── @type.ts - ├── index.ts - ├── controller-base.ts // 基础交易控制器 - ├── core.ts // 核心交易逻辑 - └── exchange.ts // 交换交易逻辑 -``` - -#### 延迟加载架构 - -SDK 体积较大(~1MB),**MUST** 使用 `await import()` 延迟加载,避免影响首屏性能: - -```typescript -// ❌ 错误:静态导入会打包进主 bundle -import { setup } from './internal' - -// ✅ 正确:动态导入,按需加载 -export async function getBioforestCore(): Promise { - const [{ setup }, genesis] = await Promise.all([ - import('./internal'), - import('./genesis-block.json'), - ]) - return setup(genesis.default, cryptoHelper, customRemark) -} -``` - -#### 构建产物分析 - -| Chunk | 大小 | 加载时机 | -|-------|------|---------| -| index.js | ~1.3MB | 首屏加载 | -| bioforest-sdk-internal.js | ~983KB | 创建交易时延迟加载 | -| genesis-block.js | ~544KB | 创建交易时延迟加载 | - -#### 依赖关系 - -``` -@bfchain/core # BioForest 链核心库(npm 包) -├── @bfchain/util # 工具函数 -└── buffer # Buffer polyfill(已在 vite.config.ts 全局配置) -``` - -#### 关键函数 - -```typescript -// 高层 API(src/services/bioforest-sdk/index.ts) -createTransferTransaction(params) // 创建转账交易 -broadcastTransaction(rpcUrl, tx) // 广播交易 -getAddressInfo(rpcUrl, address) // 获取地址信息(含二次签名状态) -verifyPayPassword(secret, pay, pk) // 验证交易密码 -setPayPassword(params) // 设置交易密码 - -// 底层 API(通过 getBioforestCore() 获取) -core.transactionController.createTransferTransactionJSON() -core.transactionController.createSignatureTransactionJSON() -core.transactionController.getTransferTransactionMinFee() -core.accountBaseHelper().createSecondSecretKeypairV2() -``` - -### 交易密码流程 - -``` -用户发起转账 - │ - ▼ -getAddressInfo() 检查 secondPublicKey - │ - ├─ 无 secondPublicKey → 直接签名 - │ - └─ 有 secondPublicKey → 需要交易密码 - │ - ▼ - 用户输入交易密码 - │ - ▼ - verifyPayPassword() 验证 - │ - ├─ 失败 → 提示错误 - │ - └─ 成功 → 使用 paySecret 签名 - │ - ▼ - createTransferTransaction() - │ - ▼ - broadcastTransaction() -``` - ---- - -## EVM 链 - -适用于:Ethereum, BSC, Polygon 等 EVM 兼容链 - -### 密钥派生 - -| 项目 | 规范 | -|-----|------| -| 算法 | secp256k1 | -| 派生路径 | m/44'/60'/0'/0/{index} | -| 地址格式 | 0x + Keccak256(publicKey)[12:32] | -| Checksum | EIP-55 大小写校验 | - -### Gas 估算 - -```typescript -interface GasEstimate { - gasLimit: bigint // 执行上限 - gasPrice: bigint // Legacy 模式 - maxFeePerGas?: bigint // EIP-1559 模式 - maxPriorityFeePerGas?: bigint -} -``` - -### 手续费档位 - -| 档位 | 说明 | 时间预估 | -|-----|------|---------| -| slow | 基础价格 × 1.0 | ~1 分钟 | -| standard | 基础价格 × 1.5 | ~30 秒 | -| fast | 基础价格 × 2.0 | ~15 秒 | - -### 代币转账(ERC-20) - -``` -1. 构建 transfer(to, amount) 调用数据 -2. 估算 Gas(合约调用通常 ~65,000) -3. 设置 to = 合约地址 -4. 设置 value = 0 -5. 设置 data = 编码后的调用数据 -``` - -### 特殊处理 - -- **MUST** 自动检测 EIP-1559 支持 -- **MUST** 正确处理 nonce 冲突 -- **SHOULD** 预留 20% Gas 余量 - ---- - -## Tron 链 - -### 密钥派生 - -| 项目 | 规范 | -|-----|------| -| 算法 | secp256k1 | -| 派生路径 | m/44'/195'/0'/0/{index} | -| 地址格式 | Base58Check(0x41 + Keccak256(publicKey)[12:32]) | -| 地址前缀 | T | - -### 资源模型 - -Tron 使用带宽和能量而非 Gas: - -| 资源 | 用途 | 获取方式 | -|-----|------|---------| -| 带宽 | 普通交易 | 免费额度 / 冻结 TRX | -| 能量 | 智能合约 | 冻结 TRX | - -### 手续费估算 - -```typescript -interface TronFeeEstimate { - bandwidth: number // 所需带宽 - energy: number // 所需能量(合约调用) - feeLimit: bigint // 最大费用(SUN) - activationFee?: bigint // 地址激活费(1.1 TRX) -} -``` - -### 地址激活 - -- **MUST** 检查目标地址是否已激活 -- **MUST** 未激活地址首次转入需额外 1.1 TRX -- **SHOULD** 在 UI 提示用户激活费用 - -### TRC-20 转账 - -类似 ERC-20,但需注意: -- 能量消耗通常 10,000-30,000 -- 需预估 feeLimit 防止失败 - ---- - -## Bitcoin 链 - -### 密钥派生 - -| 项目 | 规范 | -|-----|------| -| 算法 | secp256k1 | -| 派生路径 | m/44'/0'/0'/0/{index} (Legacy) | -| 地址格式 | P2PKH (1xxx), P2WPKH (bc1xxx) | - -### UTXO 模型 - -Bitcoin 使用 UTXO(未花费交易输出)模型: - -```typescript -interface UTXO { - txid: string - vout: number - value: bigint // satoshi - scriptPubKey: string - txHex?: string // 某些钱包需要完整交易 -} -``` - -### 交易构建 - -``` -1. 获取地址所有 UTXO -2. 选择足够的 UTXO(需覆盖金额 + 手续费) -3. 构建交易输入 -4. 构建交易输出(收款地址 + 找零地址) -5. 签名所有输入 -6. 广播交易 -``` - -### 手续费估算 - -```typescript -// 手续费 = 交易大小(vbytes) × 费率(sat/vB) -const estimatedSize = inputs * 148 + outputs * 34 + 10 -const fee = estimatedSize * feeRate -``` - -### 特殊处理 - -- **MUST** 正确处理 UTXO 选择 -- **MUST** 计算找零输出 -- **SHOULD** 支持 RBF(Replace-By-Fee) - ---- - -## 错误处理 - -### 通用错误码 - -| 错误码 | 说明 | -|-------|------| -| CHAIN_NOT_SUPPORTED | 不支持的链类型 | -| NETWORK_ERROR | 网络请求失败 | -| INSUFFICIENT_BALANCE | 余额不足 | -| INSUFFICIENT_FEE | 手续费不足 | -| INVALID_ADDRESS | 地址格式错误 | -| TRANSACTION_REJECTED | 交易被拒绝 | - -### 链特有错误 - -| 链 | 错误码 | 说明 | -|---|-------|------| -| BioForest | ADDRESS_FROZEN | 地址已冻结 | -| BioForest | PAYSECRET_REQUIRED | 需要交易密码 | -| Tron | ADDRESS_NOT_ACTIVATED | 地址未激活 | -| Tron | ENERGY_INSUFFICIENT | 能量不足 | -| EVM | NONCE_TOO_LOW | Nonce 冲突 | -| Bitcoin | UTXO_INSUFFICIENT | 可用 UTXO 不足 | - ---- - -## 参考实现 - -参考 mpay 项目中的实现: - -| 链 | 文件路径 | -|---|---------| -| BFMeta | `libs/wallet-base/services/wallet/bfmeta/` | -| Ethereum | `libs/wallet-base/services/wallet/ethereum/` | -| Tron | `libs/wallet-base/services/wallet/tron/` | -| BSC | `libs/wallet-base/services/wallet/binance/` | -| Bitcoin | `libs/wallet-base/services/wallet/bitcoin/` | diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IBiometricService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IBiometricService.md" deleted file mode 100644 index 5fd095830..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IBiometricService.md" +++ /dev/null @@ -1,164 +0,0 @@ -# IBiometricService 生物识别服务 - -> 指纹/面部识别认证 - ---- - -## 职责 - -提供生物识别认证能力,用于应用解锁和交易确认。 - ---- - -## 接口定义 - -``` -IBiometricService { - // 检查设备是否支持生物识别 - isSupported(): boolean - - // 检查用户是否已注册生物识别 - isEnrolled(): boolean - - // 获取可用的生物识别类型 - getAvailableTypes(): BiometricType[] - - // 执行认证 - authenticate(options: AuthOptions): AuthResult -} -``` - ---- - -## 数据结构 - -### BiometricType - -``` -BiometricType = 'fingerprint' | 'face' | 'iris' -``` - -### AuthOptions - -``` -AuthOptions { - reason: string // 显示给用户的认证原因 - title?: string // 弹窗标题 - fallbackEnabled: boolean // 是否允许回退到图案锁 - timeout?: number // 超时时间(秒),默认 30 -} -``` - -### AuthResult - -``` -AuthResult { - success: boolean - method?: BiometricType | 'pattern' - error?: BiometricError -} - -BiometricError = - | 'NOT_SUPPORTED' // 设备不支持 - | 'NOT_ENROLLED' // 用户未设置 - | 'LOCKOUT' // 尝试次数过多 - | 'LOCKOUT_PERMANENT' // 永久锁定 - | 'USER_CANCEL' // 用户取消 - | 'SYSTEM_CANCEL' // 系统取消 - | 'TIMEOUT' // 超时 - | 'FALLBACK' // 用户选择图案锁 -``` - ---- - -## 使用流程 - -### 启用生物识别 - -``` -1. 检查 isSupported() - │ - ├─ false → 隐藏生物识别选项 - │ - ▼ true -2. 检查 isEnrolled() - │ - ├─ false → 引导用户到系统设置 - │ - ▼ true -3. 执行 authenticate() 验证身份 - │ - ├─ 失败 → 提示错误 - │ - ▼ 成功 -4. 存储"已启用生物识别"标记 -``` - -### 解锁应用 - -``` -1. 检查用户是否启用了生物识别 - │ - ├─ 否 → 显示图案锁 - │ - ▼ 是 -2. 自动弹出 authenticate() - │ - ├─ 成功 → 解锁 - │ - ├─ 用户取消 → 显示图案锁 - │ - ▼ 错误 -3. 根据错误类型处理 -``` - ---- - -## 错误处理 - -| 错误 | 用户提示 | 后续操作 | -|-----|---------|---------| -| NOT_SUPPORTED | "您的设备不支持生物识别" | 隐藏选项 | -| NOT_ENROLLED | "请先在系统设置中注册指纹/面容" | 跳转设置 | -| LOCKOUT | "尝试次数过多,请稍后重试" | 等待解锁 | -| LOCKOUT_PERMANENT | "生物识别已锁定,请使用图案锁" | 仅图案锁 | -| USER_CANCEL | - | 显示图案锁 | -| TIMEOUT | "认证超时" | 允许重试 | - ---- - -## reason 文案规范 - -| 场景 | reason | -|-----|--------| -| 解锁应用 | "验证身份以解锁 BFM Pay" | -| 确认转账 | "验证身份以确认转账" | -| 查看助记词 | "验证身份以查看助记词" | -| 启用生物识别 | "验证身份以启用生物识别" | - ---- - -## 安全要求 - -| 要求 | 说明 | -|-----|------| -| 不存储生物特征 | 仅使用系统 API | -| 有图案锁回退 | 生物识别失败时可用图案锁 | -| 超时限制 | 最长等待 30 秒 | -| 失败次数限制 | 遵循系统策略 | - ---- - -## 平台实现 - -### Web (WebAuthn) - -- 使用 `PublicKeyCredential` API -- 创建凭证时绑定到域名 -- **限制**:需要 HTTPS - -### DWEB (Plaoc) - -- 使用 `plaoc.biometric` API -- 直接调用系统生物识别 -- 支持指纹和面容 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/ICameraService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/ICameraService.md" deleted file mode 100644 index 6f5cc43fb..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/ICameraService.md" +++ /dev/null @@ -1,201 +0,0 @@ -# ICameraService 相机服务 - -> 相机访问和二维码扫描 - ---- - -## 职责 - -提供相机访问能力,主要用于扫描二维码。 - ---- - -## 接口定义 - -``` -ICameraService { - // 检查是否支持 - isSupported(): boolean - - // 请求相机权限 - requestPermission(): PermissionStatus - - // 扫描二维码 - scanQRCode(options?: ScanOptions): ScanResult - - // 停止扫描 - stopScan(): void -} -``` - ---- - -## 数据结构 - -### ScanOptions - -``` -ScanOptions { - // 扫描区域(相对于视口) - scanArea?: { - x: number // 0-1 - y: number // 0-1 - width: number // 0-1 - height: number // 0-1 - } - - // 支持的格式 - formats?: QRFormat[] - - // 是否显示取景框 - showViewfinder?: boolean - - // 振动反馈 - hapticFeedback?: boolean -} - -QRFormat = 'QR_CODE' | 'DATA_MATRIX' | 'AZTEC' | 'PDF_417' -``` - -### ScanResult - -``` -ScanResult { - success: boolean - - // 成功时 - data?: string // 解码内容 - format?: QRFormat // 码格式 - - // 失败时 - error?: ScanError -} - -ScanError = - | 'PERMISSION_DENIED' // 权限被拒绝 - | 'NO_CAMERA' // 无可用相机 - | 'CANCELLED' // 用户取消 - | 'DECODE_FAILED' // 解码失败 -``` - ---- - -## 扫描流程 - -### 用户流程 - -``` -1. 用户点击"扫码"按钮 - │ - ▼ -2. 检查权限状态 - │ - ├─ denied → 显示权限引导 - │ - ├─ prompt → requestPermission() - │ - ▼ granted -3. 打开扫码界面 - │ - ▼ -4. 实时扫描 - │ - ├─ 识别成功 → 处理结果 - │ - ├─ 用户取消 → 关闭 - │ - ▼ -5. 返回结果 -``` - -### 扫码界面规范 - -``` -┌─────────────────────────────────┐ -│ [←] 扫一扫 │ -├─────────────────────────────────┤ -│ │ -│ ┌───────────────────┐ │ -│ │ │ │ -│ │ 取景框区域 │ │ ← 扫描区域 -│ │ (带动画边框) │ │ -│ │ │ │ -│ └───────────────────┘ │ -│ │ -│ 将二维码放入框内 │ -│ │ -├─────────────────────────────────┤ -│ [相册] [手电筒] │ -└─────────────────────────────────┘ -``` - ---- - -## 扫描内容处理 - -### 支持的内容格式 - -| 格式 | 示例 | 处理 | -|-----|------|------| -| 纯地址 | `0x1234...` | 填入地址输入框 | -| EIP-681 | `ethereum:0x1234?value=1e18` | 解析并填入转账表单 | -| BFM 协议 | `bfm://transfer?to=...` | 解析并执行 | -| DWEB 授权 | `bfmpay://authorize?...` | 跳转授权页 | - -### 解析流程 - -``` -扫描结果 - │ - ▼ -尝试解析为 URI - │ - ├─ 是 URI → 按协议处理 - │ - ▼ 不是 -尝试验证为地址 - │ - ├─ 是地址 → 填入地址框 - │ - ▼ 不是 -显示"无法识别的内容" -``` - ---- - -## 权限处理 - -### 权限被拒绝 - -``` -┌─────────────────────────────────┐ -│ │ -│ [相机图标] │ -│ │ -│ 需要相机权限才能扫码 │ -│ │ -│ 请在系统设置中允许 │ -│ BFM Pay 访问相机 │ -│ │ -│ [打开设置] │ -│ │ -└─────────────────────────────────┘ -``` - ---- - -## 性能要求 - -| 指标 | 目标值 | -|-----|-------| -| 相机启动时间 | < 1s | -| 识别延迟 | < 500ms | -| 帧率 | ≥ 30fps | - ---- - -## 可访问性 - -- **MUST** 提供手动输入作为替代 -- **SHOULD** 支持从相册选择图片 -- **SHOULD** 扫描成功时振动反馈 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IClipboardService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IClipboardService.md" deleted file mode 100644 index 28d471b98..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IClipboardService.md" +++ /dev/null @@ -1,113 +0,0 @@ -# IClipboardService 剪贴板服务 - -剪贴板服务提供跨平台的文本和数据复制粘贴能力。 - -## 接口定义 - -``` -IClipboardService { - // 写入 - writeText(text: string): Promise - writeData(data: ClipboardData): Promise - - // 读取 - readText(): Promise - readData(): Promise - - // 查询 - hasText(): Promise - getAvailableTypes(): Promise - - // 清空 - clear(): Promise -} -``` - -## 数据模型 - -### ClipboardData - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| type | string | 是 | MIME 类型 | -| text | string | 否 | 文本内容 | -| html | string | 否 | HTML 内容 | -| uri | string | 否 | URI/URL | -| blob | Blob | 否 | 二进制数据 | - -## 功能规范 - -### 文本写入 - -**writeText(text: string)** - -- MUST 将文本写入系统剪贴板 -- MUST 支持 Unicode 字符 -- MUST 在写入失败时抛出异常 -- SHOULD 支持大文本(>1MB) - -### 文本读取 - -**readText()** - -- MUST 从系统剪贴板读取文本 -- MUST 在无文本时返回空字符串 -- MUST 在无权限时抛出异常 -- SHOULD 支持多种文本编码 - -### 权限处理 - -- MUST 在首次使用时请求剪贴板权限(如平台要求) -- MUST 在权限被拒绝时提供清晰错误信息 -- SHOULD 缓存权限状态避免重复询问 - -## 使用场景 - -### 1. 复制地址 - -``` -用户点击复制按钮 - ↓ -调用 writeText(address) - ↓ -显示复制成功反馈 -``` - -### 2. 粘贴地址 - -``` -用户点击粘贴按钮 - ↓ -检查 hasText() - ↓ -调用 readText() - ↓ -验证地址格式 - ↓ -填入输入框 -``` - -## 平台适配 - -| 平台 | 实现方式 | -|------|----------| -| Web | Clipboard API | -| DWEB | dweb.clipboard API | -| iOS | UIPasteboard | -| Android | ClipboardManager | - -## 安全考虑 - -- MUST NOT 在后台自动读取剪贴板 -- MUST 在读取敏感数据后及时清空 -- SHOULD 提供剪贴板清空倒计时功能 -- MAY 支持加密剪贴板内容 - -## 错误处理 - -| 错误类型 | 说明 | 处理方式 | -|----------|------|----------| -| PermissionDenied | 无剪贴板权限 | 提示用户授权 | -| NotSupported | 平台不支持 | 使用降级方案 | -| ReadError | 读取失败 | 重试或提示 | -| WriteError | 写入失败 | 重试或提示 | diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IDeepLinkService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IDeepLinkService.md" deleted file mode 100644 index 140f3807a..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IDeepLinkService.md" +++ /dev/null @@ -1,220 +0,0 @@ -# IDeepLinkService 深度链接服务 - -> 处理外部应用发起的链接请求 - ---- - -## 职责 - -处理来自外部应用(如 DWEB 应用)的链接请求,实现跨应用交互。 - ---- - -## 接口定义 - -``` -IDeepLinkService { - // 注册链接处理器 - register(handler: DeepLinkHandler): Unsubscribe - - // 解析链接 - parse(url: string): DeepLinkData | null - - // 生成链接 - generate(data: DeepLinkData): string - - // 获取启动链接(如果有) - getInitialLink(): string | null -} -``` - ---- - -## 数据结构 - -### DeepLinkData - -``` -DeepLinkData { - action: DeepLinkAction - params: Record -} - -DeepLinkAction = - | 'transfer' // 发起转账 - | 'authorize-address' // 地址授权 - | 'authorize-signature' // 签名授权 - | 'import' // 导入钱包 - | 'connect' // 连接请求 -``` - -### DeepLinkHandler - -``` -type DeepLinkHandler = (data: DeepLinkData) => void -type Unsubscribe = () => void -``` - ---- - -## URL 格式 - -### 协议 - -``` -bfmpay://{action}?{params} -``` - -### 转账链接 - -``` -bfmpay://transfer? - to={address}& - amount={amount}& - token={token_address}& - chain={chain_id}& - memo={memo} -``` - -**参数说明**: - -| 参数 | 必需 | 说明 | -|-----|-----|------| -| to | Y | 收款地址 | -| amount | N | 金额(最小单位) | -| token | N | 代币地址(原生币为空) | -| chain | N | 链 ID | -| memo | N | 备注 | - -### 地址授权链接 - -``` -bfmpay://authorize/address? - app={app_id}& - callback={callback_url}& - chain={chain_id}& - nonce={nonce} -``` - -**参数说明**: - -| 参数 | 必需 | 说明 | -|-----|-----|------| -| app | Y | 请求应用标识 | -| callback | Y | 授权结果回调 URL | -| chain | N | 指定链(空则用户选择) | -| nonce | Y | 一次性随机数 | - -### 签名授权链接 - -``` -bfmpay://authorize/signature? - app={app_id}& - callback={callback_url}& - message={message}& - type={sign_type} -``` - -**参数说明**: - -| 参数 | 必需 | 说明 | -|-----|-----|------| -| app | Y | 请求应用标识 | -| callback | Y | 签名结果回调 URL | -| message | Y | 待签名消息(Base64) | -| type | N | 签名类型:personal/typed | - ---- - -## 处理流程 - -### 接收链接 - -``` -外部应用调用 DeepLink - │ - ▼ -应用被唤起(或已在前台) - │ - ▼ -parse() 解析链接 - │ - ├─ 解析失败 → 显示错误 - │ - ▼ 成功 -调用注册的 handler - │ - ▼ -根据 action 导航到对应页面 -``` - -### 授权流程 - -``` -1. 收到授权链接 - │ - ▼ -2. 跳转授权确认页面 - │ - ▼ -3. 用户确认/拒绝 - │ - ├─ 拒绝 → callback 返回错误 - │ - ▼ 确认 -4. 执行签名/获取地址 - │ - ▼ -5. callback 返回结果 -``` - ---- - -## 安全要求 - -### 授权请求验证 - -- **MUST** 显示请求来源(app 标识) -- **MUST** 显示请求内容(地址/签名消息) -- **MUST** 要求用户明确确认 -- **SHOULD** 记录授权历史 - -### 回调 URL 验证 - -- **MUST** 验证 callback URL 格式 -- **SHOULD** 检查 callback 域名白名单 -- **MUST NOT** 在回调中包含敏感信息 - -### nonce 防重放 - -- **MUST** 验证 nonce 未被使用 -- **MUST** 使用后标记 nonce 失效 -- nonce 有效期建议 5 分钟 - ---- - -## 错误处理 - -| 错误 | 说明 | 回调内容 | -|-----|------|---------| -| INVALID_URL | URL 格式错误 | - | -| UNKNOWN_ACTION | 不支持的操作 | - | -| USER_REJECTED | 用户拒绝 | `error=rejected` | -| INVALID_CHAIN | 不支持的链 | `error=invalid_chain` | -| TIMEOUT | 操作超时 | `error=timeout` | - ---- - -## 平台实现 - -### Web - -- 使用 URL hash 路由 -- 监听 `hashchange` 事件 -- 初始链接从 `location.hash` 获取 - -### DWEB (Plaoc) - -- 使用 Plaoc DeepLink API -- 监听 `plaoc.onDeepLink` 事件 -- 支持应用间直接调用 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IHapticService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IHapticService.md" deleted file mode 100644 index 40236c8b4..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IHapticService.md" +++ /dev/null @@ -1,177 +0,0 @@ -# IHapticService 触觉反馈服务 - -触觉反馈服务提供跨平台的振动和触觉反馈能力,增强用户交互体验。 - -## 接口定义 - -``` -IHapticService { - // 预定义反馈 - impact(style: ImpactStyle): Promise - notification(type: NotificationType): Promise - selection(): Promise - - // 自定义振动 - vibrate(pattern: number[]): Promise - - // 能力查询 - isSupported(): Promise - getSupportedStyles(): Promise - - // 控制 - enable(): void - disable(): void - isEnabled(): boolean -} -``` - -## 枚举定义 - -### ImpactStyle - -| 值 | 说明 | 使用场景 | -|----|------|----------| -| light | 轻微反馈 | 选择项目、轻触 | -| medium | 中等反馈 | 确认操作、切换 | -| heavy | 强烈反馈 | 重要操作、完成 | -| soft | 柔和反馈 | 滑动、拖拽 | -| rigid | 硬质反馈 | 碰撞、边界 | - -### NotificationType - -| 值 | 说明 | 使用场景 | -|----|------|----------| -| success | 成功通知 | 操作完成、交易成功 | -| warning | 警告通知 | 需要注意、即将超时 | -| error | 错误通知 | 操作失败、验证错误 | - -## 功能规范 - -### 冲击反馈 - -**impact(style: ImpactStyle)** - -- MUST 产生与 style 对应的触觉反馈 -- MUST 在不支持时静默失败 -- SHOULD 与视觉反馈同步触发 -- MAY 根据系统设置自动调整强度 - -### 通知反馈 - -**notification(type: NotificationType)** - -- MUST 产生与通知类型对应的触觉模式 -- MUST 区分 success/warning/error -- SHOULD 使用系统预定义的反馈模式 - -### 选择反馈 - -**selection()** - -- MUST 产生轻微的选择反馈 -- SHOULD 用于列表选择、开关切换 -- SHOULD 频率限制避免过度振动 - -### 自定义振动 - -**vibrate(pattern: number[])** - -- MUST 按模式数组执行振动 -- 数组格式:[振动时长, 暂停时长, 振动时长, ...] -- MUST 支持取消正在进行的振动 -- SHOULD 限制最大振动时长 - -## 使用场景 - -### 1. 交易成功 - -``` -交易确认完成 - ↓ -调用 notification('success') - ↓ -同时显示成功动画 -``` - -### 2. 图案锁输入 - -``` -用户输入每个数字 - ↓ -调用 impact('light') - ↓ -图案错误时 - ↓ -调用 notification('error') -``` - -### 3. 下拉刷新 - -``` -用户下拉到阈值 - ↓ -调用 impact('medium') - ↓ -释放触发刷新 -``` - -### 4. 长按操作 - -``` -长按达到激活时间 - ↓ -调用 impact('heavy') - ↓ -显示操作菜单 -``` - -## 平台适配 - -| 平台 | 实现方式 | 精度 | -|------|----------|------| -| Web | Vibration API | 低 | -| DWEB | dweb.haptic API | 高 | -| iOS | UIImpactFeedbackGenerator | 高 | -| Android | VibrationEffect | 中 | - -### 平台能力对照 - -| 能力 | Web | DWEB | iOS | Android | -|------|-----|------|-----|---------| -| impact styles | 否 | 是 | 是 | 部分 | -| notification types | 否 | 是 | 是 | 否 | -| selection | 否 | 是 | 是 | 否 | -| custom pattern | 是 | 是 | 否 | 是 | - -## 配置选项 - -``` -HapticConfig { - enabled: boolean // 全局开关 - respectSystemSetting: boolean // 遵循系统设置 - intensityScale: number // 强度缩放 0-1 -} -``` - -## 最佳实践 - -1. **适度使用** - 避免过度振动造成骚扰 -2. **提供开关** - 允许用户禁用触觉反馈 -3. **配合视觉** - 触觉应与视觉反馈同步 -4. **考虑场景** - 重要操作使用强反馈,日常操作使用轻反馈 -5. **遵循系统** - 尊重系统级别的触觉设置 - -## 错误处理 - -| 错误类型 | 说明 | 处理方式 | -|----------|------|----------| -| NotSupported | 设备不支持触觉 | 静默忽略 | -| Disabled | 用户已禁用 | 静默忽略 | -| InvalidPattern | 振动模式无效 | 使用默认模式 | - -## 可访问性 - -- MUST 提供关闭触觉反馈的选项 -- SHOULD 与屏幕阅读器配合使用 -- SHOULD 在静音模式下仍可工作 -- MAY 根据用户偏好调整强度 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IShareService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IShareService.md" deleted file mode 100644 index bfd70f030..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IShareService.md" +++ /dev/null @@ -1,153 +0,0 @@ -# IShareService 分享服务 - -分享服务提供跨平台的内容分享能力,支持文本、链接、图片等多种内容类型。 - -## 接口定义 - -``` -IShareService { - // 分享 - share(data: ShareData): Promise - - // 能力查询 - canShare(data: ShareData): Promise - getSupportedTypes(): Promise - - // 特定分享 - shareText(text: string, title?: string): Promise - shareUrl(url: string, title?: string): Promise - shareFile(file: ShareFile): Promise - shareImage(image: ImageData): Promise -} -``` - -## 数据模型 - -### ShareData - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| title | string | 否 | 分享标题 | -| text | string | 否 | 分享文本 | -| url | string | 否 | 分享链接 | -| files | ShareFile[] | 否 | 分享文件列表 | - -### ShareFile - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| name | string | 是 | 文件名 | -| type | string | 是 | MIME 类型 | -| data | Blob | 是 | 文件数据 | - -### ShareResult - -| 字段 | 类型 | 说明 | -|------|------|------| -| success | boolean | 是否成功 | -| target | string | 分享目标应用 | -| error | Error | 错误信息 | - -## 功能规范 - -### 通用分享 - -**share(data: ShareData)** - -- MUST 调用系统分享面板 -- MUST 支持至少一种内容类型 -- MUST 在用户取消时返回相应状态 -- SHOULD 支持同时分享多种内容 - -### 能力查询 - -**canShare(data: ShareData)** - -- MUST 在不触发 UI 的情况下检查分享能力 -- MUST 准确反映平台支持情况 -- SHOULD 考虑当前内容类型的兼容性 - -### 文本分享 - -**shareText(text: string)** - -- MUST 支持纯文本分享 -- MUST 支持长文本(>10KB) -- SHOULD 自动识别文本中的 URL - -## 使用场景 - -### 1. 分享收款地址 - -``` -用户点击分享按钮 - ↓ -构建 ShareData { - title: "我的收款地址", - text: address, - url: deepLink -} - ↓ -调用 share(data) - ↓ -用户选择分享目标 -``` - -### 2. 分享交易凭证 - -``` -交易完成 - ↓ -生成交易截图 - ↓ -调用 shareImage(screenshot) - ↓ -用户分享到社交平台 -``` - -### 3. 分享收款二维码 - -``` -用户点击分享二维码 - ↓ -生成二维码图片 - ↓ -调用 shareFile({ - name: "qrcode.png", - type: "image/png", - data: qrBlob -}) -``` - -## 平台适配 - -| 平台 | 实现方式 | 文件支持 | -|------|----------|----------| -| Web | Web Share API | 部分 | -| DWEB | dweb.share API | 完整 | -| iOS | UIActivityViewController | 完整 | -| Android | Intent.ACTION_SEND | 完整 | - -## 降级策略 - -当 Web Share API 不可用时: - -1. **复制到剪贴板** - 最基本的降级 -2. **生成分享链接** - 用户手动复制 -3. **显示二维码** - 供其他设备扫描 - -## 错误处理 - -| 错误类型 | 说明 | 处理方式 | -|----------|------|----------| -| NotSupported | 平台不支持分享 | 使用降级策略 | -| UserCancelled | 用户取消分享 | 静默处理 | -| PermissionDenied | 无分享权限 | 提示用户授权 | -| InvalidContent | 内容格式无效 | 检查并修正内容 | - -## 最佳实践 - -1. **预检查分享能力** - 使用 canShare 隐藏不支持的分享选项 -2. **提供降级方案** - 确保所有平台都有可用的分享方式 -3. **优化分享内容** - 根据目标平台优化文本长度 -4. **处理取消操作** - 用户取消不应视为错误 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IStorageService.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IStorageService.md" deleted file mode 100644 index ec79fb2ac..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/IStorageService.md" +++ /dev/null @@ -1,158 +0,0 @@ -# IStorageService 安全存储服务 - -> 加密的本地数据存储 - ---- - -## 职责 - -安全地存储敏感数据(如加密后的助记词、用户偏好)。 - ---- - -## 接口定义 - -``` -IStorageService { - // 存储数据 - setItem(key: string, value: string): void - - // 读取数据 - getItem(key: string): string | null - - // 删除数据 - removeItem(key: string): void - - // 检查是否存在 - hasItem(key: string): boolean - - // 获取所有键 - getAllKeys(): string[] - - // 清空所有数据 - clear(): void -} -``` - ---- - -## 存储分区 - -### 按敏感度分区 - -| 分区 | 前缀 | 加密 | 内容 | -|-----|------|-----|------| -| secure | `sec_` | 是 | 加密的助记词、私钥 | -| private | `prv_` | 否 | 用户偏好、设置 | -| cache | `cch_` | 否 | 临时缓存 | - -### 存储键命名 - -``` -{分区前缀}{模块}_{键名} - -示例: -sec_wallet_encrypted_mnemonic -prv_settings_language -cch_balance_0x1234 -``` - ---- - -## 安全要求 - -### 加密存储 - -对于 secure 分区: - -``` -存储流程: -plaintext → AES-256-GCM 加密 → Base64 编码 → 存储 - -读取流程: -存储值 → Base64 解码 → AES-256-GCM 解密 → plaintext -``` - -### 加密密钥管理 - -``` -用户图案锁 - │ - ▼ PBKDF2 (100,000 iterations) -派生密钥 (256 bits) - │ - ▼ -AES-256-GCM 加密数据 -``` - -### 密钥派生参数 - -| 参数 | 值 | -|-----|---| -| 算法 | PBKDF2 | -| 哈希 | SHA-256 | -| 迭代次数 | 100,000 | -| 输出长度 | 256 bits | -| 盐 | 随机 16 bytes,存储在 `sec_crypto_salt` | - ---- - -## 数据持久化 - -### 存储位置 - -| 平台 | 存储方式 | -|-----|---------| -| Web | IndexedDB | -| DWEB | Plaoc Secure Storage | - -### 同步策略 - -- **MUST** 写入后立即持久化 -- **MUST NOT** 依赖内存缓存 -- **SHOULD** 写入失败时抛出异常 - ---- - -## 错误处理 - -| 错误 | 说明 | 处理 | -|-----|------|------| -| STORAGE_FULL | 存储空间满 | 清理缓存 | -| QUOTA_EXCEEDED | 超出配额 | 清理缓存 | -| DECRYPT_FAILED | 解密失败 | 图案错误或数据损坏 | -| KEY_NOT_FOUND | 键不存在 | 返回 null | - ---- - -## 数据迁移 - -### 版本升级 - -``` -存储数据版本号:prv_storage_version - -迁移流程: -1. 读取当前版本 -2. 如果版本 < 目标版本 -3. 执行迁移函数 -4. 更新版本号 -``` - -### 迁移示例 - -``` -v1 → v2: - - 重命名 wallet_data → sec_wallet_encrypted_mnemonic - - 删除 deprecated_key -``` - ---- - -## 安全审计点 - -- [ ] 敏感数据使用 secure 分区 -- [ ] 加密密钥不明文存储 -- [ ] 清理数据时覆写内存 -- [ ] 不在日志中输出存储内容 -- [ ] 导出功能需要身份验证 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/index.md" deleted file mode 100644 index 118c2c79c..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/03-\345\271\263\345\217\260\346\234\215\345\212\241/index.md" +++ /dev/null @@ -1,87 +0,0 @@ -# 平台服务 - -> 跨平台能力的统一抽象 - ---- - -## 概述 - -BFM Pay 运行在多种环境(Web 浏览器、DWEB 容器),平台服务层提供统一抽象,屏蔽环境差异。 - ---- - -## 服务列表 - -| 服务 | 说明 | Web | DWEB | -|-----|------|-----|------| -| [IBiometricService](./IBiometricService) | 生物识别认证 | WebAuthn | Plaoc | -| [IStorageService](./IStorageService) | 安全存储 | IndexedDB+加密 | Plaoc | -| [IClipboardService](./IClipboardService) | 剪贴板操作 | Clipboard API | Plaoc | -| [ICameraService](./ICameraService) | 相机/扫码 | MediaDevices | Plaoc | -| [IShareService](./IShareService) | 系统分享 | Web Share | Plaoc | -| [IHapticService](./IHapticService) | 振动反馈 | Vibration API | Plaoc | -| [IDeepLinkService](./IDeepLinkService) | 深度链接 | URL handling | Plaoc | - ---- - -## 平台检测 - -``` -IPlatformDetector { - // 当前平台类型 - platform: 'web' | 'dweb' - - // 检查能力是否可用 - isAvailable(capability: Capability): boolean - - // 获取平台服务实例 - getServices(): IPlatformServices -} - -Capability = - | 'biometric' - | 'secure-storage' - | 'clipboard' - | 'camera' - | 'share' - | 'haptic' - | 'deep-link' -``` - ---- - -## 降级策略 - -当某能力不可用时: - -| 能力 | 降级方案 | -|-----|---------| -| 生物识别 | 仅使用图案锁认证 | -| 安全存储 | 使用加密的 IndexedDB | -| 相机 | 隐藏扫码,提供手动输入 | -| 分享 | 提供复制功能替代 | -| 振动 | 静默失败 | - ---- - -## 权限管理 - -### 需要权限的服务 - -| 服务 | 权限 | 请求时机 | -|-----|------|---------| -| 相机 | camera | 首次扫码时 | -| 生物识别 | biometric | 启用生物识别时 | -| 剪贴板读取 | clipboard-read | 粘贴地址时 | - -### 权限状态 - -``` -PermissionStatus = 'granted' | 'denied' | 'prompt' -``` - -### 权限被拒绝处理 - -- **MUST** 显示引导用户到系统设置 -- **MUST NOT** 反复请求被拒绝的权限 -- **SHOULD** 提供替代操作方式 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/04-\344\272\213\344\273\266\347\263\273\347\273\237/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/04-\344\272\213\344\273\266\347\263\273\347\273\237/index.md" deleted file mode 100644 index 82bedbbff..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/04-\344\272\213\344\273\266\347\263\273\347\273\237/index.md" +++ /dev/null @@ -1,294 +0,0 @@ -# 事件系统 - -> 定义数据变化的订阅机制 - ---- - -## 设计目标 - -钱包应用需要实时响应以下变化: - -- **余额变化** - 收到转账、交易确认 -- **交易状态变化** - pending → confirmed / failed -- **新区块** - 链上新区块产生 -- **汇率变化** - 法币汇率更新 - -### 设计原则 - -- **类型安全** - 事件类型和数据类型严格对应 -- **统一接口** - 所有事件源使用相同的订阅 API -- **资源管理** - 自动清理订阅,防止内存泄漏 - ---- - -## Subscribable 接口 - -### 核心接口 - -``` -Subscription { - unsubscribe(): void - readonly closed: boolean -} - -Observer { - next: (value: T) => void - error?: (error: Error) => void - complete?: () => void -} - -Subscribable { - subscribe(observer: Observer): Subscription - getCurrentValue?(): T | undefined -} -``` - -### 规范要求 - -- **MUST** 实现 subscribe 方法返回 Subscription -- **MUST** 调用 unsubscribe 后停止推送事件 -- **SHOULD** 实现 getCurrentValue 获取当前值 -- **SHOULD** 在错误时调用 observer.error - ---- - -## 事件类型定义 - -### BalanceChangeEvent 余额变化 - -| 字段 | 类型 | 说明 | -|-----|------|------| -| chainId | ChainId | 链标识 | -| address | Address | 账户地址 | -| tokenAddress | Address \| null | 代币地址(null=原生币) | -| newBalance | AssetBalance | 新余额 | -| previousBalance | AssetBalance | 旧余额(可选) | -| reason | string | 变化原因 | - -### TransactionStatusEvent 交易状态 - -| 字段 | 类型 | 说明 | -|-----|------|------| -| chainId | ChainId | 链标识 | -| hash | TxHash | 交易哈希 | -| status | Status | pending/confirmed/failed/dropped | -| confirmations | number | 确认数 | -| receipt | TxReceipt | 交易回执(可选) | - -### NewBlockEvent 新区块 - -| 字段 | 类型 | 说明 | -|-----|------|------| -| chainId | ChainId | 链标识 | -| blockNumber | bigint | 区块高度 | -| blockHash | string | 区块哈希 | -| timestamp | number | 时间戳 | - ---- - -## 订阅服务接口 - -### IAssetSubscription 资产订阅 - -``` -IAssetSubscription { - // 订阅原生代币余额 - subscribeNativeBalance(query: { - address: Address - }): Subscribable - - // 订阅代币余额 - subscribeTokenBalance(query: { - address: Address - tokenAddress: Address - }): Subscribable - - // 订阅所有资产变化 - subscribeAllAssets(query: { - address: Address - }): Subscribable -} -``` - -### ITransactionSubscription 交易订阅 - -``` -ITransactionSubscription { - // 订阅单笔交易状态 - subscribeTransactionStatus( - hash: TxHash - ): Subscribable - - // 订阅地址相关交易 - subscribeAddressTransactions( - address: Address - ): Subscribable -} -``` - -### IBlockSubscription 区块订阅 - -``` -IBlockSubscription { - // 订阅新区块 - subscribeNewBlocks(): Subscribable - - // 获取当前区块高度 - getCurrentBlockNumber(): Promise -} -``` - ---- - -## 实现策略 - -### WebSocket 订阅 - -| 事件类型 | WebSocket 消息 | 适用链 | -|---------|---------------|-------| -| 余额变化 | pendingTransactions, newBlocks | EVM | -| 交易状态 | transactionReceipt | EVM | -| 新区块 | newHeads | EVM | - -### 轮询订阅 - -当 WebSocket 不可用时的降级策略: - -| 事件类型 | 轮询间隔 | 接口 | -|---------|---------|------| -| 余额变化 | 30s | getBalance | -| 交易状态 | 5s | getTransactionReceipt | -| 新区块 | 15s | getBlockNumber | - -### 混合策略 - -- **MUST** 优先使用 WebSocket 获取实时数据 -- **MUST** WebSocket 断开时自动降级到轮询 -- **SHOULD** WebSocket 重连后恢复实时订阅 -- **SHOULD** 合并相同查询避免重复请求 - ---- - -## UI 集成规范 - -### useSubscription Hook - -``` -useSubscription( - subscribable: Subscribable | null, - initialValue: T -): T -``` - -**行为规范:** - -- **MUST** 在组件卸载时自动取消订阅 -- **MUST** subscribable 变化时重新订阅 -- **SHOULD** 支持初始值设置 -- **SHOULD** 处理订阅错误 - -### 使用模式 - -``` -组件挂载 - │ - ▼ -创建 Subscribable(使用 useMemo) - │ - ▼ -调用 useSubscription - │ - ├── 获取初始值 - │ - └── 订阅变化 - │ - ▼ - 收到新值 → 触发重渲染 - │ - ▼ -组件卸载 → 自动取消订阅 -``` - ---- - -## 与状态缓存结合 - -### 订阅驱动缓存更新 - -``` -订阅事件 - │ - ▼ -收到新数据 - │ - ▼ -更新状态缓存(Query Cache) - │ - ▼ -依赖该缓存的组件自动更新 -``` - -### 缓存键映射 - -| 事件类型 | 缓存键 | -|---------|-------| -| 原生余额 | ['asset', 'native', chainId, address] | -| 代币余额 | ['asset', 'token', chainId, address, tokenAddress] | -| 交易状态 | ['transaction', 'status', hash] | - ---- - -## 资源管理规范 - -### 订阅生命周期 - -- **MUST** 组件卸载时取消所有订阅 -- **MUST** 重复订阅相同数据时复用连接 -- **SHOULD** 无订阅者时释放 WebSocket 连接 -- **MAY** 实现订阅引用计数 - -### 错误处理 - -| 错误类型 | 处理方式 | -|---------|---------| -| 连接断开 | 自动重连 + 降级轮询 | -| 订阅失败 | 调用 observer.error | -| 数据解析错误 | 记录日志 + 跳过 | - -### 重连策略 - -``` -连接断开 - │ - ▼ -等待 1s - │ - ▼ -尝试重连 - │ - ├── 成功 ──► 恢复订阅 - │ - └── 失败 ──► 等待 2s ──► 重试(指数退避) - │ - └── 最大 30s -``` - ---- - -## 最佳实践 - -1. **缓存 Subscribable** - 使用 useMemo 避免重复创建 -2. **清理订阅** - 使用 useEffect 清理函数 -3. **处理错误** - 提供 error 回调处理异常 -4. **合并请求** - 相同数据使用单个订阅 -5. **降级策略** - 准备轮询作为后备方案 - ---- - -## 本章小结 - -- Subscribable 提供统一的订阅接口 -- 支持余额、交易状态、新区块等事件 -- 优先 WebSocket,降级轮询 -- 与状态缓存结合实现自动更新 -- 完善的资源管理防止内存泄漏 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/05-Mock\346\234\215\345\212\241\350\247\204\350\214\203/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/05-Mock\346\234\215\345\212\241\350\247\204\350\214\203/index.md" deleted file mode 100644 index c734bf59e..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/05-Mock\346\234\215\345\212\241\350\247\204\350\214\203/index.md" +++ /dev/null @@ -1,366 +0,0 @@ -# Mock 服务规范 - -> 定义 Mock 服务的使用规范和调试工具 - ---- - -## 1. 概述 - -Mock 服务用于开发和测试环境,提供模拟数据和行为。正确使用 Mock 服务可以: -- 加速开发迭代(不依赖真实后端) -- 支持离线开发 -- 便于测试边界条件 -- 提供可控的调试环境 - ---- - -## 2. 核心原则 - -### 2.1 编译时替换 - -Mock 实现 **MUST** 通过 Vite alias 在编译时选择,**MUST NOT** 在业务代码中直接导入: - -```typescript -// ❌ 错误:直接导入 mock 实现 -import { mockStakingService } from '@/services/staking.mock' - -// ✅ 正确:通过 alias 导入(编译时替换) -import { stakingService } from '@/services/staking' -``` - -### 2.2 环境变量控制 - -通过 `SERVICE_IMPL` 环境变量选择实现: - -| 值 | 说明 | 命令 | -|---|------|------| -| `web` | Web 平台实现(默认) | `pnpm dev` | -| `dweb` | DWEB 平台实现 | `SERVICE_IMPL=dweb pnpm dev` | -| `mock` | Mock 实现 | `pnpm dev:mock` | - -### 2.3 接口一致性 - -Mock 实现 **MUST** 实现与真实服务相同的接口: - -```typescript -// types.ts - 定义接口 -export interface IStakingService { - getOverview(): Promise - submitMint(request: MintRequest): Promise - // ... -} - -// mock.ts - Mock 实现 -export class StakingService implements IStakingService { - async getOverview(): Promise { - // Mock 实现 - } -} - -// web.ts - Web 实现 -export class StakingService implements IStakingService { - async getOverview(): Promise { - // 真实实现 - } -} -``` - ---- - -## 3. 服务目录结构 - -每个服务 **MUST** 遵循以下目录结构: - -``` -services/ - └── staking/ - ├── types.ts # 接口定义 - ├── web.ts # Web 平台实现 - ├── dweb.ts # DWEB 平台实现 - ├── mock.ts # Mock 实现 - ├── index.ts # 统一导出(编译时替换) - └── __tests__/ # 测试 - ├── mock.test.ts - └── web.test.ts -``` - -### index.ts 模板 - -```typescript -/** - * Staking 服务 - * - * 通过 Vite alias 在编译时选择实现: - * - web: Web 平台 - * - dweb: DWEB 平台 - * - mock: Mock 实现(自动包装并注册到 MockDevTools) - */ - -export type { IStakingService, StakingOverviewItem } from './types' - -// 编译时通过 Vite alias 替换 -// #staking-impl -> ./web.ts | ./dweb.ts | ./mock.ts -// 注意:直接导出单例,mock.ts 导出的是已包装的服务 -export { stakingService } from '#staking-impl' -``` - -### web.ts / dweb.ts 模板 - -```typescript -export class StakingService implements IStakingService { - // 实现接口... -} - -// 导出单例 -export const stakingService = new StakingService() -``` - -### mock.ts 模板 - -```typescript -import { defineMockMeta, wrapMockService } from '@/services/mock-devtools' - -class StakingService implements IStakingService { - // 实现接口... -} - -// 定义 Mock 元信息(用于 MockDevTools) -export const stakingMockMeta = defineMockMeta((b) => - b.name('staking') - .description('质押服务') - .methods((m) => m - .method('getOverview', (mm) => mm.description('获取概览').input(z.undefined()).output(OverviewSchema)) - ) -) - -// 内部实例 -const _instance = new StakingService() - -// 导出包装后的单例(自动注册到 MockDevTools) -export const stakingService = wrapMockService(stakingMockMeta, _instance) -``` - ---- - -## 4. Mock 数据管理 - -### 4.1 数据存储 - -Mock 数据 **SHOULD** 存储在模块级变量中,**MAY** 支持持久化到 localStorage: - -```typescript -// mock.ts -let mockData: StakingOverviewItem[] = [...initialMockData] - -export class StakingService implements IStakingService { - async getOverview(): Promise { - return mockData - } - - // Mock 专用:重置数据 - _resetData(): void { - mockData = [...initialMockData] - } -} -``` - -### 4.2 Mock 控制器 - -Mock 实现 **MAY** 暴露控制方法(以下划线开头),用于调试: - -```typescript -export class StakingService implements IStakingService { - // 公开接口 - async getOverview(): Promise { ... } - - // Mock 控制方法 - _setMockData(data: StakingOverviewItem[]): void { ... } - _resetData(): void { ... } - _simulateError(error: Error): void { ... } - _setDelay(ms: number): void { ... } -} -``` - ---- - -## 5. MockDevTools - -### 5.1 概述 - -MockDevTools 是一个可视化调试工具,**ONLY** 在 `SERVICE_IMPL=mock` 时可用: - -``` -┌─────────────────────────────────────────────────┐ -│ MockDevTools [×] │ -├─────────────────────────────────────────────────┤ -│ Services │ -│ ├── 📦 staking (active) │ -│ │ ├── getOverview: 3 calls │ -│ │ └── submitMint: 1 call │ -│ ├── 📦 transaction (active) │ -│ └── 📦 wallet (active) │ -├─────────────────────────────────────────────────┤ -│ Quick Actions │ -│ [Reset All Data] [Simulate Error] [Add Delay] │ -├─────────────────────────────────────────────────┤ -│ Request Log │ -│ 10:30:15 staking.getOverview → 200 (45ms) │ -│ 10:30:18 staking.submitMint → 200 (120ms) │ -└─────────────────────────────────────────────────┘ -``` - -### 5.2 功能 - -| 功能 | 说明 | -|-----|------| -| 服务状态 | 显示所有 Mock 服务及其调用统计 | -| 数据编辑 | 编辑/重置 Mock 数据 | -| 错误模拟 | 模拟网络错误、超时 | -| 延迟控制 | 添加人工延迟测试加载状态 | -| 请求日志 | 记录所有服务调用 | - -### 5.3 集成方式 - -```tsx -// App.tsx -import { MockDevTools } from '@/services/mock-devtools' - -export function App() { - return ( - <> - - {import.meta.env.DEV && } - - ) -} -``` - ---- - -## 6. 组件使用规范 - -### 6.1 必须通过服务层调用 - -组件 **MUST** 通过服务层调用平台能力,**MUST NOT** 直接调用浏览器 API: - -```typescript -// ❌ 错误:直接调用浏览器 API -const handleCopy = async () => { - await navigator.clipboard.writeText(address) -} - -// ✅ 正确:通过服务层调用 -import { clipboardService } from '@/services/clipboard' - -const handleCopy = async () => { - await clipboardService.write(address) -} -``` - -### 6.2 使用 Hook 封装 - -推荐使用 Hook 获取服务,便于测试和 Mock: - -```typescript -// hooks.ts -export function useClipboard() { - return clipboardService -} - -// Component.tsx -function AddressDisplay({ address }: Props) { - const clipboard = useClipboard() - - const handleCopy = async () => { - await clipboard.write(address) - } -} -``` - -### 6.3 禁止直接调用的 API 列表 - -| 浏览器 API | 应使用的服务 | -|-----------|-------------| -| `navigator.clipboard` | `clipboardService` | -| `navigator.vibrate` | `hapticsService` | -| `localStorage` / `sessionStorage` | `storageService` | -| `navigator.mediaDevices` | `cameraService` | -| `WebAuthn` | `biometricService` | - ---- - -## 7. 常见错误 - -### 7.1 直接导入 Mock - -```typescript -// ❌ 错误 -import { mockStakingService } from '@/services/staking.mock' -const data = await mockStakingService.getOverview() - -// ✅ 正确 -import { stakingService } from '@/services/staking' -const data = await stakingService.getOverview() -``` - -### 7.2 Hook 中生成 Mock 数据 - -```typescript -// ❌ 错误:Hook 中直接生成假数据 -function useTransactionHistory() { - const [data] = useState(() => generateMockTransactions()) - return { data } -} - -// ✅ 正确:通过 Service 获取数据 -function useTransactionHistory() { - const [data, setData] = useState([]) - useEffect(() => { - transactionService.getHistory().then(setData) - }, []) - return { data } -} -``` - -### 7.3 组件中硬编码假数据 - -```typescript -// ❌ 错误:组件中硬编码 -const MOCK_BALANCES = { ETH: { BFM: '1000.00' } } - -// ✅ 正确:通过 Service 获取 -const balances = await assetService.getBalances(address) -``` - ---- - -## 8. 迁移指南 - -对于现有代码中的假数据问题,按以下步骤修复: - -1. **识别问题代码**:搜索 `mock` 关键字 -2. **创建/完善 Service**:确保服务遵循标准结构 -3. **更新 Vite 配置**:添加 alias -4. **更新组件**:使用标准导入 -5. **测试**:在 mock 和 web 模式下都能正常工作 - ---- - -## 9. 检查清单 - -在 PR 合并前,请确认: - -- [ ] 没有直接导入 `*.mock.ts` 文件 -- [ ] 没有在组件/Hook 中硬编码假数据 -- [ ] 没有直接调用浏览器 API(如 `navigator.clipboard`) -- [ ] 新服务遵循标准目录结构(index.ts 导出单例) -- [ ] Mock 实现使用 `wrapMockService` 包装 -- [ ] Mock 实现与接口一致 -- [ ] 在 `pnpm dev` 和 `pnpm dev:mock` 下都能正常工作 -- [ ] MockDevTools 能显示服务调用日志 - ---- - -## 下一章 - -继续阅读 [事件系统](../04-事件系统/)。 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/06-\346\234\215\345\212\241\345\256\232\344\271\211\347\263\273\347\273\237/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/06-\346\234\215\345\212\241\345\256\232\344\271\211\347\263\273\347\273\237/index.md" deleted file mode 100644 index 66ea4976a..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/06-\346\234\215\345\212\241\345\256\232\344\271\211\347\263\273\347\273\237/index.md" +++ /dev/null @@ -1,821 +0,0 @@ -# 服务定义系统 - -> Schema-first 的服务定义与实现规范 - ---- - -## 1. 概述 - -服务定义系统采用 **Schema-first** 设计模式,通过统一的元信息定义来约束所有平台实现,并通过中间件模式处理横切关注点(日志、拦截、Mock 等)。 - -### 1.1 核心理念 - -``` -Schema Definition → Type-safe Implementation → Middleware Composition - (types.ts) (web/dweb/mock.ts) (可组合的中间件) -``` - -### 1.2 设计目标 - -| 目标 | 说明 | -|------|------| -| 单一数据源 | 服务定义只写一次,类型自动推导 | -| 类型安全 | TypeScript 验证实现是否符合定义 | -| 关注点分离 | 日志/拦截/Mock 通过中间件实现,不侵入业务代码 | -| 平台一致性 | web/dweb/mock 三个平台被同一 Schema 约束 | - ---- - -## 2. 成员类型 - -服务由多种类型的成员组成,每种类型有其特定的语义和使用场景。 - -### 2.1 类型概览 - -| 类型 | 语义 | 签名模式 | 典型场景 | -|------|------|----------|----------| -| `api` | 请求-响应 | `(input) => Promise` | 外部请求、RPC 调用 | -| `get` | 只读属性 | `() => T` | 获取状态 | -| `set` | 只写属性 | `(value: T) => void` | 设置状态 | -| `getset` | 读写属性 | `get/set` 组合 | 可读写状态 | -| `stream` | 事件流 | `() => AsyncIterable` | 订阅、推送 | -| `method` | 通用方法 | 任意签名 | 复杂场景 | - -### 2.2 选择指南 - -``` -大部分情况 → api / get / set / getset / stream -复杂情况 → method (多参数、重载、特殊签名) -``` - -**api vs method:** -- `api`: 简单纯粹,单输入单输出,适合定义"对外部的请求" -- `method`: 万能但复杂,支持多参数、可选参数、重载等 - ---- - -## 3. API 设计 - -### 3.1 定义服务元信息 - -```typescript -// services/clipboard/types.ts -import { z } from 'zod' -import { defineServiceMeta } from '@/lib/service-meta' - -// 简写形式 (推荐) -export const clipboardServiceMeta = defineServiceMeta('clipboard', (s) => - s.description('剪贴板服务') - .api('write', z.object({ text: z.string() }), z.void()) - .api('read', z.void(), z.string()) -) - -// 或使用 options 对象形式 (需要 description 时) -export const clipboardServiceMeta = defineServiceMeta('clipboard', (s) => - s.description('剪贴板服务') - .api('write', { - description: '写入剪贴板', - input: z.object({ text: z.string() }), - output: z.void(), - }) - .api('read', { - description: '读取剪贴板', - input: z.void(), - output: z.string(), - }) -) - -// 导出类型 (自动推导) -export type IClipboardService = typeof clipboardServiceMeta.Type -``` - -### 3.2 实现服务 - -```typescript -// services/clipboard/web.ts -import { clipboardServiceMeta } from './types' - -export const clipboardService = clipboardServiceMeta.impl({ - async write({ text }) { - await navigator.clipboard.writeText(text) - }, - async read() { - return navigator.clipboard.readText() - }, -}) -``` - -### 3.3 中间件 - -```typescript -// 全局日志中间件 (MockDevTools) -clipboardServiceMeta.use(async (ctx, next) => { - const start = Date.now() - try { - const result = await next() - addLog({ - service: ctx.service, - member: ctx.member, - type: ctx.type, // 'api' | 'get' | 'set' | 'stream' | 'method' - input: ctx.input, - output: result, - duration: Date.now() - start, - }) - return result - } catch (error) { - addLog({ ...ctx, error, duration: Date.now() - start }) - throw error - } -}) -``` - ---- - -## 4. 成员详细定义 - -### 4.1 api - -最常用的类型,用于定义请求-响应式的异步操作。 - -```typescript -s.api('methodName', { - description: '方法描述', // 可选 - input: z.object({ ... }), // 输入 Schema - output: z.object({ ... }), // 输出 Schema -}) -``` - -**实现签名:** -```typescript -async methodName(input: Input): Promise -``` - -**示例:** -```typescript -// 定义 -s.api('getBalance', { - description: '获取余额', - input: z.object({ address: z.string() }), - output: z.object({ balance: z.string(), symbol: z.string() }), -}) - -// 实现 -async getBalance({ address }) { - const balance = await rpc.getBalance(address) - return { balance: balance.toString(), symbol: 'ETH' } -} -``` - -### 4.2 get - -只读属性,用于获取状态。 - -```typescript -s.get('propertyName', { - description: '属性描述', - type: z.string(), // 属性类型 Schema -}) -``` - -**实现签名:** -```typescript -get propertyName(): T -// 或 -propertyName(): T -``` - -**示例:** -```typescript -// 定义 -s.get('isAvailable', { - description: '生物识别是否可用', - type: z.boolean(), -}) - -// 实现 -get isAvailable() { - return 'credentials' in navigator -} -``` - -### 4.3 set - -只写属性,用于设置状态。 - -```typescript -s.set('propertyName', { - description: '属性描述', - type: z.string(), -}) -``` - -**实现签名:** -```typescript -set propertyName(value: T) -// 或 -setPropertyName(value: T): void -``` - -### 4.4 getset - -读写属性,组合 get 和 set。 - -```typescript -s.getset('propertyName', { - description: '属性描述', - type: z.enum(['light', 'dark']), -}) -``` - -**实现签名:** -```typescript -{ - get propertyName(): T - set propertyName(value: T) -} -``` - -### 4.5 stream - -事件流,用于订阅持续的数据更新。 - -```typescript -s.stream('streamName', { - description: '流描述', - yield: z.object({ ... }), // 每次 yield 的数据类型 - return: z.void(), // 可选,流结束时的返回值 -}) -``` - -**实现签名:** -```typescript -streamName(): AsyncIterable -// 或 -async *streamName(): AsyncGenerator -``` - -**示例:** -```typescript -// 定义 -s.stream('onBalanceChange', { - description: '余额变化事件流', - yield: z.object({ - address: z.string(), - balance: z.string(), - timestamp: z.number(), - }), -}) - -// 实现 -async *onBalanceChange() { - const ws = new WebSocket('wss://...') - for await (const event of ws) { - yield JSON.parse(event.data) - } -} -``` - -### 4.6 method - -通用方法,用于复杂场景。 - -```typescript -s.method('methodName', { - description: '方法描述', - args: z.tuple([ArgSchema1, ArgSchema2]), // 参数元组 - output: OutputSchema, - async: true, // 是否异步,默认 true -}) -``` - -**实现签名:** -```typescript -methodName(arg1: Arg1, arg2: Arg2): Promise -// 或 (async: false) -methodName(arg1: Arg1, arg2: Arg2): Output -``` - -**示例:** -```typescript -// 定义 - 复杂签名 -s.method('signTransaction', { - description: '签名交易', - args: z.tuple([ - TransactionSchema, - z.object({ gasLimit: z.bigint().optional() }), - ]), - output: SignedTransactionSchema, -}) - -// 实现 -async signTransaction(tx, options) { - const gasLimit = options?.gasLimit ?? await estimateGas(tx) - return sign(tx, gasLimit) -} -``` - ---- - -## 5. 中间件系统 - -### 5.1 中间件签名 - -```typescript -type Middleware = (ctx: MiddlewareContext, next: () => Promise) => Promise - -interface MiddlewareContext { - service: string // 服务名 - member: string // 成员名 - type: MemberType // 'api' | 'get' | 'set' | 'stream' | 'method' - input: unknown // 输入参数 - meta: MemberMeta // 成员元信息 -} -``` - -### 5.2 注册中间件 - -```typescript -// 服务级中间件 -clipboardServiceMeta.use(loggingMiddleware) - -// 全局中间件 (所有服务) -ServiceMeta.useGlobal(loggingMiddleware) -``` - -### 5.3 中间件执行顺序 - -``` -Request → Global Middleware 1 → Global Middleware 2 → Service Middleware → Implementation - ↓ -Response ← Global Middleware 1 ← Global Middleware 2 ← Service Middleware ← Result -``` - -### 5.4 内置中间件 - -| 中间件 | 功能 | -|--------|------| -| `loggingMiddleware` | 记录请求日志 | -| `validationMiddleware` | 校验输入输出 Schema | -| `timingMiddleware` | 记录执行耗时 | -| `retryMiddleware` | 失败重试 | -| `cacheMiddleware` | 结果缓存 | - ---- - -## 6. 目录结构 - -``` -services/ - clipboard/ - types.ts # 服务元信息定义 (defineServiceMeta) - web.ts # Web 平台实现 - dweb.ts # DWEB 平台实现 (或复用 web.ts) - mock.ts # Mock 实现 - index.ts # 统一导出 - lib/ - service-meta/ - index.ts # 主入口 - builder.ts # ServiceMetaBuilder - types.ts # 类型定义 - logging-middleware.ts # 日志中间件 -``` - ---- - -## 6.1 DWEB 与 Web 的关系 - -**DWEB 是 Web 的超集**。大部分服务直接复用 Web 实现: - -```typescript -// services/staking/dweb.ts -// DWEB 是 Web 的超集,直接复用 Web 实现 -export { stakingService } from './web' -``` - -**只有使用 plaoc 插件的服务才需要独立实现:** - -| 服务 | DWEB 实现 | 原因 | -|------|-----------|------| -| clipboard | 独立 | 使用 clipboardPlugin | -| biometric | 独立 | 使用 biometricsPlugin | -| storage | 独立 | 使用 keychainPlugin | -| toast | 独立 | 使用 toastPlugin | -| haptics | 独立 | 使用 hapticsPlugin | -| authorize | 独立 | 使用 dwebServiceWorker | -| staking | 复用 web | 纯 HTTP API | -| transaction | 复用 web | 纯 HTTP API | -| camera | 复用 web | 无 plaoc 插件 | -| currency-exchange | 复用 web | 纯 HTTP API | - ---- - -## 7. 完整示例 - -### 7.1 定义 (types.ts) - -```typescript -import { z } from 'zod' -import { defineServiceMeta } from '@/lib/service-meta' - -// Schema 定义 -const BiometricTypeSchema = z.enum(['fingerprint', 'face', 'none']) -const BiometricAvailabilitySchema = z.object({ - isAvailable: z.boolean(), - biometricType: BiometricTypeSchema, - error: z.string().optional(), -}) -const BiometricResultSchema = z.object({ - success: z.boolean(), - errorMessage: z.string().optional(), -}) - -// 服务元信息 -export const biometricServiceMeta = defineServiceMeta('biometric', (s) => - s - .description('生物识别服务') - .api('isAvailable', { - description: '检查生物识别是否可用', - input: z.void(), - output: BiometricAvailabilitySchema, - }) - .api('verify', { - description: '执行生物识别验证', - input: z.object({ - reason: z.string().optional(), - fallbackEnabled: z.boolean().optional(), - }), - output: BiometricResultSchema, - }) -) - -// 导出类型 -export type IBiometricService = typeof biometricServiceMeta.Type -export type BiometricType = z.infer -export type BiometricAvailability = z.infer -export type BiometricResult = z.infer -``` - -### 7.2 Web 实现 (web.ts) - -```typescript -import { biometricServiceMeta } from './types' - -export const biometricService = biometricServiceMeta.impl({ - async isAvailable() { - if (!window.PublicKeyCredential) { - return { isAvailable: false, biometricType: 'none' } - } - try { - const available = await PublicKeyCredential - .isUserVerifyingPlatformAuthenticatorAvailable() - return { - isAvailable: available, - biometricType: available ? 'fingerprint' : 'none', - } - } catch { - return { isAvailable: false, biometricType: 'none' } - } - }, - - async verify({ reason, fallbackEnabled }) { - // WebAuthn 实现... - return { success: false, errorMessage: 'Not implemented in web' } - }, -}) -``` - -### 7.3 Mock 实现 (mock.ts) - -```typescript -import { biometricServiceMeta } from './types' - -// Mock 数据 -let mockAvailable = true -let mockBiometricType: 'fingerprint' | 'face' | 'none' = 'fingerprint' - -export const biometricService = biometricServiceMeta.impl({ - async isAvailable() { - return { - isAvailable: mockAvailable, - biometricType: mockBiometricType, - } - }, - - async verify() { - if (!mockAvailable) { - return { success: false, errorMessage: 'Biometric not available' } - } - return { success: true } - }, -}) - -// Mock 控制器 (可选) -export const biometricMockController = { - setAvailable(available: boolean) { mockAvailable = available }, - setBiometricType(type: typeof mockBiometricType) { mockBiometricType = type }, -} -``` - -### 7.4 导出 (index.ts) - -```typescript -export type { - IBiometricService, - BiometricType, - BiometricAvailability, - BiometricResult, -} from './types' - -export { biometricServiceMeta } from './types' -export { biometricService } from '#biometric-impl' -``` - ---- - -## 8. MockDevTools 集成 - -### 8.1 全局日志中间件注册 - -```typescript -// frontend-main.tsx -// Mock 模式下注册全局日志中间件 -if (__MOCK_MODE__) { - import('./lib/service-meta').then(({ ServiceMeta, loggingMiddleware }) => { - ServiceMeta.useGlobal(loggingMiddleware) - }) -} -``` - -### 8.2 内置日志中间件 - -```typescript -// lib/service-meta/logging-middleware.ts -export const loggingMiddleware: Middleware = async (ctx, next) => { - const startTime = Date.now() - const log = addLog({ - timestamp: new Date(), - service: ctx.service, - member: ctx.member, - type: ctx.type, - input: ctx.input, - status: 'pending', - }) - - try { - const result = await next() - updateLog(log.id, { - output: result, - duration: Date.now() - startTime, - status: 'success', - }) - return result - } catch (error) { - updateLog(log.id, { - error: error instanceof Error ? error.message : String(error), - duration: Date.now() - startTime, - status: 'error', - }) - throw error - } -} -``` - -### 8.3 自定义拦截中间件 - -```typescript -// 用于测试特定场景 -biometricServiceMeta.use(async (ctx, next) => { - // 模拟网络延迟 - if (ctx.member === 'verify') { - await new Promise(r => setTimeout(r, 500)) - } - - // 模拟错误 - if (shouldSimulateError(ctx)) { - throw new Error('Simulated error') - } - - return next() -}) -``` - ---- - -## 9. 全局设置系统 - -MockDevTools 提供全局设置来控制所有 Mock 服务的行为。 - -### 9.1 设置 API - -```typescript -import { - getGlobalDelay, - setGlobalDelay, - getGlobalError, - setGlobalError, - resetSettings, - subscribeSettings, -} from '@/lib/service-meta' - -// 设置全局延迟 (所有请求都会延迟) -setGlobalDelay(500) // 500ms - -// 设置全局错误 (所有请求都会抛出错误) -setGlobalError(new Error('Network Error')) - -// 清除全局错误 -setGlobalError(null) - -// 重置所有设置 -resetSettings() - -// 订阅设置变更 -const unsubscribe = subscribeSettings((settings) => { - console.log('Settings changed:', settings) -}) -``` - -### 9.2 设置中间件 - -设置通过 `settingsMiddleware` 应用到所有请求: - -```typescript -// frontend-main.tsx -if (__MOCK_MODE__) { - import('./lib/service-meta').then(({ - ServiceMeta, - loggingMiddleware, - settingsMiddleware, - breakpointMiddleware - }) => { - // 执行顺序: logging → settings → breakpoint → 实现 - ServiceMeta.useGlobal(loggingMiddleware) - ServiceMeta.useGlobal(settingsMiddleware) - ServiceMeta.useGlobal(breakpointMiddleware) - }) -} -``` - ---- - -## 10. 断点调试系统 - -断点系统允许在请求的输入/输出阶段暂停执行,用于调试。 - -### 10.1 断点配置 - -```typescript -import { - setBreakpoint, - removeBreakpoint, - getBreakpoints, - clearBreakpoints, -} from '@/lib/service-meta' - -// 设置断点 -setBreakpoint({ - service: 'clipboard', - method: 'write', - delayMs: 500, // 可选:延迟执行 - input: { - pause: true, // 在输入阶段暂停 - pauseCondition: '$input.text.length > 10', // 条件表达式 - inject: '$input.text = "modified"', // 注入代码 - }, - output: { - pause: true, // 在输出阶段暂停 - }, -}) - -// 移除断点 -removeBreakpoint('clipboard', 'write') - -// 清除所有断点 -clearBreakpoints() -``` - -### 10.2 暂停请求管理 - -```typescript -import { - getPausedRequests, - resumePausedRequest, - abortPausedRequest, - subscribeBreakpoints, -} from '@/lib/service-meta' - -// 获取所有暂停的请求 -const paused = getPausedRequests() - -// 继续执行 (可修改输入/输出) -resumePausedRequest(request.id, { - $input: { text: 'modified input' }, -}) - -// 中止请求 -abortPausedRequest(request.id, new Error('Aborted')) - -// 订阅断点变更 -subscribeBreakpoints(() => { - console.log('Breakpoints or paused requests changed') -}) -``` - -### 10.3 Console 面板 - -Console 面板提供表达式执行环境,可直接操作暂停的请求: - -| 变量 | 说明 | -|------|------| -| `$paused` | 所有暂停请求的数组 | -| `$0`, `$1`, ... | 按索引访问暂停请求 | -| `$_` | 最新的暂停请求 | -| `$p{id}` | 按 ID 访问,如 `$p1`, `$p2` | - -**可用方法:** -```javascript -$p1.$input // 查看输入 -$p1.$output // 查看输出 (仅 output 阶段) -$p1.resume() // 继续执行 -$p1.resume({ $input: { text: 'new' } }) // 修改后继续 -$p1.abort() // 中止请求 -``` - -**内置命令:** -| 命令 | 说明 | -|------|------| -| `/help` | 显示帮助 | -| `/clear` | 清空 Console | -| `/vars` | 显示可用变量 | -| `/copy ` | 复制表达式结果到剪贴板 | - -### 10.4 IndexedDB 持久化 - -断点配置自动保存到 IndexedDB,刷新页面后恢复。 - ---- - -## 11. 迁移指南 - -### 11.1 从旧架构迁移 - -**旧架构:** -```typescript -// types.ts -export interface IClipboardService { - write(text: string): Promise - read(): Promise -} - -// web.ts -export class ClipboardService implements IClipboardService { - async write(text: string) { ... } - async read() { ... } -} -export const clipboardService = new ClipboardService() -``` - -**新架构:** -```typescript -// types.ts -export const clipboardServiceMeta = defineServiceMeta('clipboard', (s) => - s - .api('write', { input: z.object({ text: z.string() }), output: z.void() }) - .api('read', { input: z.void(), output: z.string() }) - .build() -) -export type IClipboardService = typeof clipboardServiceMeta.Type - -// web.ts -export const clipboardService = clipboardServiceMeta.impl({ - async write({ text }) { ... }, - async read() { ... }, -}) -``` - -### 11.2 迁移步骤 - -1. 创建 `defineServiceMeta` 定义 -2. 更新 web.ts 使用 `.impl()` -3. 更新 dweb.ts 使用 `.impl()` -4. 更新 mock.ts 使用 `.impl()` -5. 删除旧的 `wrapMockService` 调用 -6. 注册全局日志中间件 - ---- - -## 12. 检查清单 - -- [ ] 服务使用 `defineServiceMeta` 定义元信息 -- [ ] 所有平台使用 `.impl()` 实现 -- [ ] input/output 使用 Zod Schema 定义 -- [ ] 复杂方法才使用 `method`,简单场景使用 `api` -- [ ] 属性使用 `get`/`set`/`getset` -- [ ] 事件流使用 `stream` -- [ ] Mock 模式下全局日志中间件已注册 - ---- - -## 下一章 - -继续阅读 [事件系统](../04-事件系统/)。 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/07-\351\223\276\351\205\215\347\275\256\347\256\241\347\220\206/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/07-\351\223\276\351\205\215\347\275\256\347\256\241\347\220\206/index.md" deleted file mode 100644 index 53b9701fb..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/07-\351\223\276\351\205\215\347\275\256\347\256\241\347\220\206/index.md" +++ /dev/null @@ -1,302 +0,0 @@ -# 链配置管理服务 - -> 定义多链配置的获取、存储和管理规范 - ---- - -## 概述 - -链配置管理服务负责管理应用支持的区块链网络配置。支持三种配置来源:内置默认、订阅源、手动添加。 - ---- - -## 配置来源 - -### 优先级规则 - -当多个来源存在相同 `id` 的配置时,按以下优先级合并: - -``` -手动添加 (manual) > 订阅源 (subscription) > 内置默认 (default) -``` - -### 来源说明 - -| 来源 | 说明 | 可删除 | -|-----|------|--------| -| default | 内置于应用的默认配置 | 否 | -| subscription | 从订阅 URL 获取的配置 | 切换订阅源时清除 | -| manual | 用户手动添加的配置 | 是 | - ---- - -## 订阅源管理 - -### 订阅 URL 规范 - -- **MUST** 支持 `default` 作为特殊值,表示仅使用内置配置 -- **MUST** 验证 URL 使用 `http://` 或 `https://` 协议 -- **SHOULD** 支持 ETag 进行条件请求,避免重复下载 - -### 订阅刷新机制 - -| 触发条件 | 行为 | -|---------|------| -| 应用启动 | 检查 lastUpdated,超过刷新间隔则刷新 | -| 用户手动刷新 | 强制刷新,忽略 ETag | -| 切换订阅 URL | 清除旧订阅数据,获取新数据 | - -### 刷新流程 - -``` -1. 检查当前订阅 URL - │ - ├─ URL = "default" → 跳过,使用内置配置 - │ - └─ URL 有效 → 发起 HTTP GET 请求 - │ - ├─ 带 If-None-Match: {etag}(如有) - │ - ├─ 响应 304 → 使用缓存数据 - │ - ├─ 响应 200 → 解析并存储新配置 - │ - └─ 错误 → 使用缓存数据 + 显示错误提示 -``` - -### 订阅响应格式 - -```json -{ - "version": "1.0", - "lastUpdate": "2024-01-15T00:00:00Z", - "chains": [ - { /* ChainConfig */ }, - { /* ChainConfig */ } - ] -} -``` - -或直接返回配置数组: - -```json -[ - { /* ChainConfig */ }, - { /* ChainConfig */ } -] -``` - ---- - -## 链配置结构 - -### ChainConfig Schema - -```typescript -interface ChainConfig { - // 必需字段 - id: string // 唯一标识,格式: /^[a-z0-9-]+$/ - version: string // 配置版本,格式: "major.minor" - chainKind: ChainKind // 链类型(严格枚举,不支持 custom) - name: string // 显示名称 - symbol: string // 原生代币符号 - decimals: number // 原生代币精度 - - // 可选字段 - icon?: string // 图标 URL - prefix?: string // 地址前缀(bioforest 链使用) - rpcUrl?: string // RPC 端点 - explorerUrl?: string // 区块浏览器 URL - - // 运行时字段(不存储在配置源中) - enabled: boolean // 用户是否启用 - source: ChainConfigSource // 配置来源 -} - -type ChainKind = 'bioforest' | 'evm' | 'bitcoin' | 'tron' -type ChainConfigSource = 'default' | 'subscription' | 'manual' -``` - -### 版本兼容性 - -| 版本 major | 说明 | -|-----------|------| -| 1 | 当前支持的版本 | -| 2+ | 未来扩展,需要应用升级 | - -当配置的 major 版本高于应用支持的版本时: -- **MUST** 显示警告提示 -- **MUST** 禁用该链的启用开关 -- **SHOULD** 提示用户升级应用 - ---- - -## 链类型说明 - -### bioforest - -BioForest 生态链,使用 Ed25519 密钥派生: - -| 字段 | 说明 | -|-----|------| -| prefix | 地址前缀,默认 "b" | -| decimals | 通常为 8 | - -**支持的链**: BFMeta, CCChain, PMChain, BFChainV2, BTGMeta, BIWMeta, ETHMeta, Malibu - -### evm - -EVM 兼容链,使用 BIP44 派生(coin type 60): - -| 字段 | 说明 | -|-----|------| -| rpcUrl | RPC 端点(必需用于交易) | -| explorerUrl | 区块浏览器 URL | -| chainId | EVM chain ID(可从 RPC 获取) | - -**支持的链**: Ethereum, BSC, Polygon 等 - -### bitcoin - -Bitcoin 网络,使用 BIP84 派生(Native SegWit): - -| 字段 | 说明 | -|-----|------| -| decimals | 8 | -| 地址格式 | `bc1q...` (Native SegWit P2WPKH) | - -**派生路径**: `m/84'/0'/account'/0/index` - -### tron - -Tron 网络,使用 BIP44 派生(coin type 195): - -| 字段 | 说明 | -|-----|------| -| decimals | 6 | -| 地址格式 | `T...` (Base58Check) | - -**派生路径**: `m/44'/195'/account'/0/index` - ---- - -## 严格类型验证 - -从版本 2.0.0 起,`chainKind` 采用严格枚举验证: - -- **MUST** 使用 `bioforest`、`evm`、`bitcoin`、`tron` 之一 -- **MUST NOT** 使用 `custom` 或 `bip39`(已废弃) -- 未知类型将导致配置加载失败,而不是静默降级 - ---- - -## 用户操作 - -### 启用/禁用链 - -- 用户可以启用或禁用任何链 -- 禁用的链 **MUST NOT** 出现在链选择器中 -- 禁用状态存储在本地偏好设置中 - -### 手动添加链 - -1. 用户输入 JSON 格式的链配置 -2. 支持单个对象或数组 -3. 验证 JSON 格式和必需字段 -4. 如果 `id` 已存在,提示用户确认替换 - -### 删除手动链 - -- 仅 `source: manual` 的链可以删除 -- 删除后恢复到订阅源或默认配置(如有) - ---- - -## 存储规范 - -### IndexedDB 结构 - -| Store | Key | 内容 | -|-------|-----|------| -| chain_configs | `{source}:{id}` | 链配置记录 | -| chain_preferences | `enabledMap` | 启用状态映射 | -| chain_preferences | `subscriptionMeta` | 订阅元数据 | - -### 数据迁移 - -当应用升级需要修改存储结构时: -- **MUST** 提供向后兼容的读取逻辑 -- **SHOULD** 在首次启动时执行迁移 - ---- - -## 错误处理 - -| 错误场景 | 处理方式 | -|---------|---------| -| 订阅 URL 无效 | 拒绝保存,提示格式错误 | -| 订阅请求超时 | 使用缓存数据,显示警告 | -| 订阅响应解析失败 | 使用缓存数据,记录错误日志 | -| 手动 JSON 格式错误 | 拒绝添加,提示具体错误 | - ---- - -## 接口定义 - -```typescript -// 初始化服务,加载所有配置 -initialize(): Promise - -// 设置订阅 URL -setSubscriptionUrl(url: string): Promise - -// 刷新订阅 -refreshSubscription(): Promise<{ result: FetchSubscriptionResult; snapshot: ChainConfigSnapshot }> - -// 添加手动配置 -addManualConfig(input: string | object): Promise - -// 设置链启用状态 -setChainEnabled(id: string, enabled: boolean): Promise - -// 获取启用的链列表 -getEnabledChains(snapshot: ChainConfigSnapshot): ChainConfig[] - -// 根据 ID 获取链配置 -getChainById(snapshot: ChainConfigSnapshot, id: string): ChainConfig | null -``` - ---- - -## 与其他服务的关系 - -``` -┌─────────────────────┐ -│ ChainConfigService │ -└──────────┬──────────┘ - │ 提供链配置 - ▼ -┌─────────────────────┐ -│ AdapterRegistry │ ← 根据 type 选择适配器 -└──────────┬──────────┘ - │ - ┌─────┴─────┐ - ▼ ▼ -┌─────────┐ ┌─────────┐ -│BFMAdapter│ │EVMAdapter│ -└─────────┘ └─────────┘ -``` - ---- - -## 安全考虑 - -- **MUST NOT** 在日志中输出完整的 rpcUrl(可能含 API Key) -- **SHOULD** 验证 rpcUrl 指向可信域名 -- **MAY** 提供 RPC 端点健康检查 - ---- - -## 参考 - -- [附录 B: 链网络列表](../../附录/B-链网络列表/) - 预置链配置详情 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/08-\351\222\261\345\214\205\346\225\260\346\215\256\345\255\230\345\202\250/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/08-\351\222\261\345\214\205\346\225\260\346\215\256\345\255\230\345\202\250/index.md" deleted file mode 100644 index 1a9858ed4..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/08-\351\222\261\345\214\205\346\225\260\346\215\256\345\255\230\345\202\250/index.md" +++ /dev/null @@ -1,347 +0,0 @@ -# 钱包数据存储服务 - -> 定义钱包敏感数据的安全存储规范 - ---- - -## 概述 - -钱包数据存储服务负责安全地存储和管理钱包相关的敏感数据,包括: -- 加密的助记词/私钥 -- 钱包元数据 -- 链地址信息 -- 资产数据 - ---- - -## 架构设计 - -### 存储层次 - -``` -┌─────────────────────────────────────────────────┐ -│ WalletStorageService │ -├─────────────────────────────────────────────────┤ -│ ┌─────────────────┐ ┌─────────────────────┐ │ -│ │ SecureStorage │ │ IndexedDB │ │ -│ │ (加密敏感数据) │ │ (非敏感大数据) │ │ -│ └─────────────────┘ └─────────────────────┘ │ -│ ▼ ▼ │ -│ ┌─────────────────────────────────────────┐ │ -│ │ localStorage / IDB │ │ -│ └─────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────┘ -``` - -### 数据分类 - -| 类别 | 存储位置 | 加密 | 示例 | -|-----|---------|------|------| -| 敏感数据 | SecureStorage | AES-GCM | 助记词、私钥 | -| 钱包元数据 | IndexedDB | 否 | 钱包名称、地址、创建时间 | -| 资产数据 | IndexedDB | 否 | 余额、代币列表 | -| 偏好设置 | localStorage | 否 | 当前钱包ID、选中的链 | - ---- - -## 数据结构 - -### 钱包用户信息 (WalleterInfo) - -```typescript -interface WalleterInfo { - /** 钱包使用者名称 */ - name: string - /** 图案哈希(用于验证) */ - patternHash: string - /** 图案提示 */ - patternTips?: string - /** 当前激活的钱包ID */ - activeWalletId: string - /** 是否启用生物识别 */ - biometricEnabled: boolean - /** 是否启用钱包锁 */ - walletLockEnabled: boolean - /** 用户协议已阅读 */ - agreementAccepted: boolean - /** 创建时间 */ - createdAt: number -} -``` - -### 钱包信息 (WalletInfo) - -```typescript -interface WalletInfo { - /** 钱包唯一ID */ - id: string - /** 钱包名称 */ - name: string - /** 密钥类型 */ - keyType: 'mnemonic' | 'arbitrary' | 'privateKey' - /** 主链 */ - primaryChain: string - /** 主地址 */ - primaryAddress: string - /** 是否已备份 */ - isBackedUp: boolean - /** 创建时间 */ - createdAt: number - /** 更新时间 */ - updatedAt: number -} -``` - -### 链地址信息 (ChainAddressInfo) - -```typescript -interface ChainAddressInfo { - /** 地址Key (walletId:chain) */ - addressKey: string - /** 钱包ID */ - walletId: string - /** 链标识 */ - chain: string - /** 地址 */ - address: string - /** 公钥(可选) */ - publicKey?: string - /** 派生路径(BIP44) */ - derivationPath?: string - /** 资产列表 */ - assets: AssetInfo[] - /** 是否自定义过资产 */ - isCustomAssets: boolean - /** 是否冻结 */ - isFrozen: boolean -} -``` - -### 资产信息 (AssetInfo) - -```typescript -interface AssetInfo { - /** 资产类型标识 */ - assetType: string - /** 资产符号 */ - symbol: string - /** 精度 */ - decimals: number - /** 余额 */ - balance: string - /** 合约地址(代币) */ - contractAddress?: string - /** Logo URL */ - logoUrl?: string -} -``` - ---- - -## 存储键规范 - -### SecureStorage 键 - -| 键模式 | 说明 | 示例 | -|--------|------|------| -| `mnemonic:{walletId}` | 加密的助记词 | `mnemonic:uuid-1234` | -| `privateKey:{addressKey}` | 加密的私钥 | `privateKey:uuid:ethereum` | - -### IndexedDB 存储 - -| Store名称 | 键 | 值类型 | -|-----------|-----|--------| -| walleter | `main` | WalleterInfo | -| wallets | walletId | WalletInfo | -| chainAddresses | addressKey | ChainAddressInfo | -| addressBook | bookId | AddressBookEntry | - ---- - -## 接口定义 - -```typescript -interface IWalletStorageService { - // ==================== 初始化 ==================== - - /** 初始化存储服务 */ - initialize(): Promise - - /** 检查是否已初始化 */ - isInitialized(): boolean - - // ==================== 钱包用户 ==================== - - /** 保存钱包用户信息 */ - saveWalleterInfo(info: WalleterInfo): Promise - - /** 获取钱包用户信息 */ - getWalleterInfo(): Promise - - // ==================== 钱包管理 ==================== - - /** 创建钱包(同时存储加密助记词) */ - createWallet( - wallet: WalletInfo, - mnemonic: string, - patternKey: string // 钱包锁图案密钥 - ): Promise - - /** 获取钱包信息 */ - getWallet(walletId: string): Promise - - /** 获取所有钱包 */ - getAllWallets(): Promise - - /** 更新钱包信息 */ - updateWallet(walletId: string, updates: Partial): Promise - - /** 删除钱包 */ - deleteWallet(walletId: string): Promise - - // ==================== 助记词/私钥 ==================== - - /** 获取解密的助记词 */ - getMnemonic(walletId: string, patternKey: string): Promise - - /** 更新助记词加密(钱包锁变更时) */ - updateMnemonicEncryption( - walletId: string, - oldPatternKey: string, - newPatternKey: string - ): Promise - - /** 存储私钥 */ - savePrivateKey( - addressKey: string, - privateKey: string, - patternKey: string // 钱包锁图案密钥 - ): Promise - - /** 获取解密的私钥 */ - getPrivateKey(addressKey: string, patternKey: string): Promise - - // ==================== 链地址 ==================== - - /** 保存链地址信息 */ - saveChainAddress(info: ChainAddressInfo): Promise - - /** 获取链地址信息 */ - getChainAddress(addressKey: string): Promise - - /** 获取钱包的所有链地址 */ - getWalletChainAddresses(walletId: string): Promise - - /** 更新资产信息 */ - updateAssets(addressKey: string, assets: AssetInfo[]): Promise - - // ==================== 数据管理 ==================== - - /** 清除所有数据 */ - clearAll(): Promise - - /** 导出钱包数据(不含敏感信息) */ - exportWalletData(): Promise -} -``` - ---- - -## 安全要求 - -### 加密规范 - -1. **助记词加密** - - 使用 AES-256-GCM - - 密钥由用户图案通过 PBKDF2 派生 - - 每次加密使用随机 IV - -2. **图案验证** - - 存储图案哈希而非明文 - - 使用 PBKDF2 + 随机盐 - -3. **内存安全** - - 解密后的敏感数据使用后立即清零 - - 避免在日志中输出敏感信息 - -### DWEB 环境增强 - -在 DWEB 环境中: -- 优先使用生物识别保护 -- 使用系统 Keychain/Keystore -- 支持生物识别解锁 - ---- - -## 数据迁移 - -### 版本管理 - -```typescript -const STORAGE_VERSION = 1 - -interface StorageMetadata { - version: number - createdAt: number - lastMigratedAt?: number -} -``` - -### 迁移策略 - -1. 启动时检查版本号 -2. 如果版本低于当前版本,执行迁移 -3. 迁移完成后更新版本号 -4. 迁移失败时保留原数据,记录错误 - -### 从 localStorage 迁移 - -```typescript -async function migrateFromLocalStorage(): Promise { - const oldData = localStorage.getItem('bfm_wallets') - if (!oldData) return - - // 解析旧数据 - const { wallets, currentWalletId } = JSON.parse(oldData) - - // 迁移到新存储 - for (const wallet of wallets) { - await walletStorageService.saveWallet(wallet) - } - - // 更新偏好设置 - await walletStorageService.setActiveWalletId(currentWalletId) - - // 清理旧数据 - localStorage.removeItem('bfm_wallets') -} -``` - ---- - -## 错误处理 - -| 错误码 | 说明 | 处理建议 | -|--------|------|----------| -| STORAGE_NOT_INITIALIZED | 存储未初始化 | 调用 initialize() | -| WALLET_NOT_FOUND | 钱包不存在 | 检查钱包ID | -| DECRYPTION_FAILED | 解密失败 | 图案错误或数据损坏 | -| STORAGE_FULL | 存储空间满 | 清理缓存数据 | -| MIGRATION_FAILED | 迁移失败 | 保留原数据,记录错误 | - ---- - -## 测试要点 - -1. **加密正确性**:加密后能正确解密 -2. **图案验证**:错误图案应抛出异常 -3. **数据持久化**:重启后数据保持 -4. **迁移测试**:旧数据能正确迁移 -5. **并发安全**:多个操作不冲突 - ---- - -## 下一步 - -- [IStorageService](../03-平台服务/IStorageService.md) - 底层存储抽象 -- [安全篇](../../06-安全篇/) - 安全规范 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/09-\350\201\224\347\263\273\344\272\272\346\234\215\345\212\241/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/09-\350\201\224\347\263\273\344\272\272\346\234\215\345\212\241/index.md" deleted file mode 100644 index 9e73365d7..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/09-\350\201\224\347\263\273\344\272\272\346\234\215\345\212\241/index.md" +++ /dev/null @@ -1,296 +0,0 @@ -# 联系人服务 - -> 管理用户的联系人和地址簿 - ---- - -## 概述 - -联系人服务提供地址簿管理功能,支持: -- 一个联系人保存多个地址(不同链) -- 自动识别地址格式 -- 地址输入时的联系人建议 -- 联系人搜索和过滤 - ---- - -## 数据模型 - -### Contact(联系人) - -```typescript -interface Contact { - id: string // UUID - name: string // 联系人名称 - avatar?: string // 头像(可选,emoji 或图片URL) - addresses: ContactAddress[] // 多个地址 - memo?: string // 备注 - createdAt: number // 创建时间 - updatedAt: number // 更新时间 -} -``` - -### ContactAddress(联系人地址) - -```typescript -interface ContactAddress { - id: string // UUID - address: string // 地址 - chainType: ChainType // 链类型(自动检测或手动指定) - label?: string // 地址标签(如"主地址"、"交易所") - isDefault?: boolean // 是否默认地址 -} -``` - ---- - -## 地址格式识别 - -### 支持的格式 - -| 链类型 | 地址格式 | 示例 | -|-------|---------|------| -| Ethereum | 0x + 40位hex | `0x742d35Cc...` | -| BSC | 同 Ethereum | `0x742d35Cc...` | -| TRON | T + 33位base58 | `TJYs...` | -| Bitcoin Legacy | 1 + 25-34位base58 | `1A1zP1...` | -| Bitcoin P2SH | 3 + 25-34位base58 | `3J98t1...` | -| Bitcoin Bech32 | bc1 + 25-89位 | `bc1qar0...` | -| BFMeta | b + base58check | `b7ADmv...` | -| CCChain | c + base58check | `cXXXXX...` | -| PMChain | p + base58check | `pXXXXX...` | - -### 检测算法 - -```typescript -function detectAddressFormat(address: string): AddressFormatInfo { - // 1. 检查 0x 前缀 → Ethereum/BSC - // 2. 检查 T 前缀 + 长度 → TRON - // 3. 检查 1/3/bc1 前缀 → Bitcoin - // 4. 检查 BioForest 前缀 (b/c/p/B/g/w/e/m) → 对应链 - // 5. 返回 unknown -} -``` - ---- - -## 服务接口 - -### IContactService - -```typescript -interface IContactService { - // 联系人 CRUD - getContacts(): Contact[] - getContactById(id: string): Contact | undefined - addContact(contact: Omit): Contact - updateContact(id: string, updates: Partial): void - deleteContact(id: string): void - - // 地址操作 - addAddressToContact(contactId: string, address: ContactAddress): void - removeAddressFromContact(contactId: string, addressId: string): void - - // 搜索和建议 - searchContacts(query: string): Contact[] - suggestContacts(partialAddress: string): ContactSuggestion[] - getContactByAddress(address: string): Contact | undefined - - // 导入导出 - importContacts(contacts: Contact[]): number - exportContacts(): Contact[] -} -``` - -### ContactSuggestion(联系人建议) - -```typescript -interface ContactSuggestion { - contact: Contact - matchedAddress: ContactAddress - matchType: 'exact' | 'prefix' | 'name' - score: number // 匹配分数,用于排序 -} -``` - ---- - -## 地址输入增强 - -### AddressInput 组件增强 - -``` -┌──────────────────────────────────────────────┐ -│ [输入地址...] [扫描] [粘贴] │ -├──────────────────────────────────────────────┤ -│ 👤 Alice │ -│ b7ADmv... (BFMeta) │ -├──────────────────────────────────────────────┤ -│ 👤 Bob │ -│ 0x742d... (Ethereum) │ -├──────────────────────────────────────────────┤ -│ [👥 查看全部联系人...] │ -└──────────────────────────────────────────────┘ -``` - -### 交互流程 - -1. **聚焦即展开**:用户聚焦输入框时,自动展开建议面板 -2. **默认显示**:即使无输入,显示最近使用 + 所有联系人地址 -3. **链类型过滤**:根据当前选择的链类型过滤显示的地址 -4. **实时搜索**:输入时实时过滤匹配的联系人(按名称或地址前缀) -5. **选择填入**:用户选择联系人后,自动填入对应地址 -6. **联系人入口**:面板底部显示"查看全部联系人"按钮,跳转到 ContactPickerActivity - -### 建议排序规则 - -1. 精确匹配(地址完全相同)→ 最高优先级 -2. 前缀匹配(地址以输入开头) -3. 名称匹配(联系人名称包含输入) -4. 最近使用(按 `updatedAt` 降序) -5. 同链优先(当前链的地址排在前面) - ---- - -## ContactPickerActivity - -联系人选择器页面,用于完整浏览和选择联系人。 - -### 功能 - -- 显示所有联系人列表 -- 支持搜索过滤 -- 按链类型过滤(可选) -- 选择联系人地址后返回到调用页面 - -### 入口 - -- AddressInput 建议面板底部的"查看全部联系人"按钮 -- 可通过 `push('ContactPickerActivity', { chainType, onSelect })` 调用 - -### UI 结构 - -``` -┌──────────────────────────────────────────────┐ -│ ← 选择联系人 [链过滤 ▼] │ -├──────────────────────────────────────────────┤ -│ [🔍 搜索联系人...] │ -├──────────────────────────────────────────────┤ -│ 👤 Alice │ -│ b7ADmv... (BFMeta) [默认] │ -│ 0x742d... (Ethereum) │ -├──────────────────────────────────────────────┤ -│ 👤 Bob │ -│ TJYs... (TRON) │ -└──────────────────────────────────────────────┘ -``` - ---- - -## 存储 - -使用 localStorage 持久化,key 为 `bfm_contacts`。 - -```typescript -// 存储格式 -{ - version: 2, // 数据版本,用于迁移 - contacts: Contact[] -} -``` - -### 数据迁移 - -从 v1(单地址)迁移到 v2(多地址): - -```typescript -function migrateV1ToV2(v1Contact: V1Contact): Contact { - return { - ...v1Contact, - addresses: [{ - id: crypto.randomUUID(), - address: v1Contact.address, - chainType: v1Contact.chain ?? detectAddressFormat(v1Contact.address).chainType, - }] - } -} -``` - ---- - -## 与转账流程集成 - -### 发送页面 - -1. 地址输入框显示联系人建议 -2. 选择联系人时,如果有多个地址,弹出地址选择器 -3. 如果联系人只有一个地址,直接填入 - -### 交易历史 - -1. 显示交易对方时,优先显示联系人名称 -2. 提供"添加到联系人"操作 - ---- - -## 联系人分享与扫码添加 - -### 联系人协议 - -支持通过二维码分享和添加联系人。 - -#### JSON 格式(推荐) - -```json -{ - "type": "contact", - "name": "张三", - "addresses": [ - { "chainType": "ethereum", "address": "0x742d35Cc..." }, - { "chainType": "bitcoin", "address": "bc1qar0..." } - ], - "memo": "好友", - "avatar": "👨‍💼" -} -``` - -#### URI 格式 - -``` -contact://张三?eth=0x742d35Cc...&btc=bc1qar0...&memo=好友 -``` - -### 分享名片流程 - -1. 在通讯录页面,点击联系人菜单选择"分享" -2. 打开 `ContactShareJob` 显示联系人二维码 -3. 可选择下载图片或分享给他人 - -### 扫码添加流程 - -1. 扫描联系人二维码 -2. 解析为 `contact` 类型 -3. 打开 `ContactAddConfirmJob` 确认界面 -4. 用户确认后保存到通讯录 - -### 相关组件 - -- `ContactShareJob`: 分享联系人名片二维码 -- `ContactAddConfirmJob`: 扫码后确认添加联系人 -- 详见 [Scanner 组件](../../05-组件篇/02-通用组件/Scanner.md) - ---- - -## 安全考虑 - -- 联系人数据不加密(非敏感数据) -- 删除联系人需要验证钱包锁(防止误操作) -- 导出功能可选加密 - ---- - -## 相关链接 - -- [08-钱包数据存储](../08-钱包数据存储/) - 数据存储规范 -- [ITransactionService](../02-链服务/ITransactionService.md) - 交易服务 -- [Scanner 组件](../../05-组件篇/02-通用组件/Scanner.md) - 二维码扫描与解析 diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/index.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/index.md" deleted file mode 100644 index c53dd1c03..000000000 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/index.md" +++ /dev/null @@ -1,111 +0,0 @@ -# 第四篇:服务篇 - -> 定义产品的「数据怎么流动」 - ---- - -## 概述 - -服务篇定义 BFM Pay 与外部系统(区块链、平台 API)交互的标准接口,采用适配器模式屏蔽底层差异。 - ---- - -## 章节导航 - -### [01-服务架构](./01-服务架构/) - -定义服务层的整体架构模式。 - -**要点**: -- 适配器模式 -- 注册中心 -- 服务发现 -- 依赖注入 - -### [02-链服务](./02-链服务/) - -定义与区块链交互的标准接口。 - -| 服务 | 说明 | -|-----|------| -| [IIdentityService](./02-链服务/IIdentityService) | 地址派生、签名 | -| [IAssetService](./02-链服务/IAssetService) | 资产余额查询 | -| [ITransactionService](./02-链服务/ITransactionService) | 交易构建与广播 | -| [IChainService](./02-链服务/IChainService) | 链信息查询 | -| [IStakingService](./02-链服务/IStakingService) | 质押操作 | - -### [03-平台服务](./03-平台服务/) - -定义跨平台能力的抽象接口。 - -| 服务 | 说明 | -|-----|------| -| [IBiometricService](./03-平台服务/IBiometricService) | 生物识别 | -| [IStorageService](./03-平台服务/IStorageService) | 安全存储 | -| [ICameraService](./03-平台服务/ICameraService) | 相机/扫码 | -| [IDeepLinkService](./03-平台服务/IDeepLinkService) | 深度链接 | - -### [04-事件系统](./04-事件系统/) - -定义服务间的事件通信机制。 - -**要点**: -- 订阅模式 -- 事件定义 -- 错误处理 - -### [05-Mock服务规范](./05-Mock服务规范/) - -定义 Mock 服务的实现规范,用于开发和测试。 - -### [06-服务定义系统](./06-服务定义系统/) - -Schema-first 的服务定义系统,提供类型安全和 Mock 控制。 - -### [07-链配置管理](./07-链配置管理/) - -定义多链配置的获取、存储和管理规范。 - -**要点**: -- 三种配置来源:内置默认、订阅源、手动添加 -- 订阅源刷新机制 -- 链启用/禁用管理 -- 版本兼容性处理 - -### [08-钱包数据存储](./08-钱包数据存储/) - -定义钱包敏感数据的安全存储规范。 - -**要点**: -- 敏感数据加密存储(助记词、私钥) -- IndexedDB 存储非敏感数据 -- 数据迁移策略 -- DWEB 生物识别增强 - -### [09-联系人服务](./09-联系人服务/) - -定义联系人和地址簿的管理规范。 - -**要点**: -- 一个联系人支持多个地址 -- 自动识别地址格式(检测链类型) -- 地址输入时的联系人建议 -- 与转账流程的集成 - ---- - -## 设计原则 - -| 原则 | 说明 | -|-----|------| -| 接口优先 | 先定义接口,再实现 | -| 异步优先 | 涉及 I/O 的方法返回 Promise | -| 类型安全 | 参数和返回值有明确类型 | -| 错误明确 | 使用错误码而非字符串 | -| 可测试 | 支持 Mock 替换 | - ---- - -## 下一篇 - -完成服务篇后,继续阅读 [第五篇:组件篇](../05-组件篇/)。 diff --git "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Button.md" "b/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Button.md" deleted file mode 100644 index 02f010029..000000000 --- "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Button.md" +++ /dev/null @@ -1,158 +0,0 @@ -# Button 按钮 - -> 触发动作的交互元素 - ---- - -## 功能描述 - -用户点击后触发特定动作的交互元素。 - ---- - -## 属性规范 - -| 属性 | 类型 | 必需 | 默认值 | 说明 | -|-----|------|-----|-------|------| -| label | string | Y | - | 按钮文本 | -| variant | enum | N | 'primary' | 视觉变体 | -| size | enum | N | 'md' | 尺寸 | -| disabled | boolean | N | false | 禁用状态 | -| loading | boolean | N | false | 加载状态 | -| icon | Icon | N | - | 前置图标 | -| iconPosition | enum | N | 'left' | 图标位置:left, right | -| fullWidth | boolean | N | false | 是否占满宽度 | - -### variant 变体 - -| 值 | 说明 | 使用场景 | -|---|------|---------| -| primary | 主要操作,强调色背景 | 确认、提交 | -| secondary | 次要操作,边框样式 | 取消、返回 | -| destructive | 危险操作,红色系 | 删除、退出 | -| ghost | 无边框,仅文字 | 链接、辅助操作 | -| gradient | 渐变背景 | 品牌强调 | - -### size 尺寸 - -| 值 | 高度 | 使用场景 | -|---|------|---------| -| sm | 32px | 列表项、紧凑布局 | -| md | 44px | 默认,大多数场景 | -| lg | 52px | 页面主操作 | - ---- - -## 状态机 - -``` - ┌─────────────┐ - │ │ - ┌──────────▼──────────┐ │ - │ Idle │──┘ - └──────────┬──────────┘ - │ - ┌────────────┼────────────┐ - │ │ │ - ▼ ▼ ▼ - ┌─────────┐ ┌─────────┐ ┌─────────┐ - │ Pressed │ │ Loading │ │Disabled │ - └────┬────┘ └─────────┘ └─────────┘ - │ - ▼ - ┌─────────┐ - │ Idle │ - └─────────┘ -``` - -### 状态说明 - -| 状态 | 触发条件 | 视觉表现 | -|-----|---------|---------| -| Idle | 默认状态 | 正常显示 | -| Pressed | 用户按下 | 背景变深/缩放 | -| Loading | loading=true | 显示加载指示器,禁用交互 | -| Disabled | disabled=true | 降低透明度,禁用交互 | - ---- - -## 行为规范 - -### 必须 (MUST) - -1. 按下时显示视觉反馈(颜色变化或缩放 0.98) -2. disabled 状态时阻止所有交互事件 -3. loading 状态时显示加载指示器并禁用交互 -4. 有明确的 accessible name - -### 建议 (SHOULD) - -1. 支持键盘 Enter/Space 触发 -2. loading 时保持按钮宽度不变 -3. 有图标时,图标与文字间距一致 - -### 可选 (MAY) - -1. 支持长按操作 -2. 支持触觉反馈(振动) - ---- - -## 可访问性 - -| 要求 | 说明 | -|-----|------| -| role | button | -| aria-label | 无文本时必须提供 | -| aria-disabled | disabled 时设为 true | -| aria-busy | loading 时设为 true | -| 焦点指示器 | 聚焦时显示明显边框 | - ---- - -## 使用示例 - -### 主要操作 - -``` -[确认转账] ← primary, lg, fullWidth -``` - -### 次要操作 - -``` -[取消] [确认] ← secondary + primary -``` - -### 图标按钮 - -``` -[← 返回] ← ghost + icon -``` - -### 加载状态 - -``` -[○ 发送中...] ← loading=true -``` - ---- - -## 设计标注 - -### 尺寸规格 - -| 尺寸 | 高度 | 水平内边距 | 字号 | 圆角 | -|-----|------|-----------|-----|------| -| sm | 32px | 12px | 14px | 6px | -| md | 44px | 16px | 16px | 8px | -| lg | 52px | 24px | 18px | 10px | - -### 颜色规格 - -| 变体 | 背景色 | 文字色 | 按压态 | -|-----|-------|-------|-------| -| primary | --color-primary | white | 加深 10% | -| secondary | transparent | --color-foreground | --color-muted | -| destructive | --color-destructive | white | 加深 10% | -| ghost | transparent | --color-foreground | --color-muted | diff --git "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Card.md" "b/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Card.md" deleted file mode 100644 index 3798e9826..000000000 --- "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Card.md" +++ /dev/null @@ -1,146 +0,0 @@ -# Card 卡片 - -> 内容容器,提供视觉分组 - ---- - -## 功能描述 - -用于将相关内容组织在一起的容器组件,提供视觉边界和层次感。 - ---- - -## 属性规范 - -| 属性 | 类型 | 必需 | 默认值 | 说明 | -|-----|------|-----|-------|------| -| children | node | Y | - | 卡片内容 | -| variant | enum | N | 'default' | 视觉变体 | -| pressable | boolean | N | false | 是否可点击 | -| onPress | () => void | N | - | 点击回调 | -| padding | enum | N | 'md' | 内边距 | - -### variant 变体 - -| 值 | 说明 | 使用场景 | -|---|------|---------| -| default | 有背景色,无边框 | 常规内容卡片 | -| outline | 有边框,透明背景 | 选择项、列表项 | -| elevated | 有阴影,强调层次 | 悬浮卡片、重要内容 | -| ghost | 无边框无背景 | 仅作为分组容器 | - -### padding 内边距 - -| 值 | 规格 | 使用场景 | -|---|------|---------| -| none | 0 | 完全自定义 | -| sm | 12px | 紧凑布局 | -| md | 16px | 默认 | -| lg | 24px | 宽松布局 | - ---- - -## 行为规范 - -### 必须 (MUST) - -1. 提供明确的视觉边界 -2. pressable=true 时响应点击事件 -3. pressable=true 时有按压反馈 - -### 建议 (SHOULD) - -1. pressable=true 时 cursor 为 pointer -2. 内容溢出时正确处理(裁剪或滚动) - -### 可选 (MAY) - -1. 支持渐变背景 -2. 支持自定义边框颜色 - ---- - -## 可访问性 - -| 场景 | 要求 | -|-----|------| -| pressable=true | role="button",可通过键盘触发 | -| 包含链接 | 确保链接可单独聚焦 | - ---- - -## 组合结构 - -### CardHeader - -卡片头部区域,通常包含标题和操作。 - -``` -┌─────────────────────────────────┐ -│ 标题 [操作] │ -│ 副标题 │ -└─────────────────────────────────┘ -``` - -### CardContent - -卡片主内容区域。 - -### CardFooter - -卡片底部区域,通常包含操作按钮。 - -``` -┌─────────────────────────────────┐ -│ ... │ -├─────────────────────────────────┤ -│ [取消] [确认] │ -└─────────────────────────────────┘ -``` - ---- - -## 设计标注 - -### 尺寸规格 - -| 部分 | 规格 | -|-----|------| -| 圆角 | 12px | -| 边框 | 1px(outline 变体) | -| 阴影 | 0 4px 12px rgba(0,0,0,0.1)(elevated 变体) | - -### 颜色规格 - -| 变体 | 背景色 | 边框色 | -|-----|-------|-------| -| default | --color-card | - | -| outline | transparent | --color-border | -| elevated | --color-card | - | -| ghost | transparent | - | - ---- - -## 使用示例 - -### 信息卡片 - -``` -┌─────────────────────────────────┐ -│ 总资产 │ -│ ¥ 12,345.67 │ -│ +2.5% 今日 │ -└─────────────────────────────────┘ -``` - -### 可点击列表项 - -``` -┌─────────────────────────────────┐ -│ [图标] 设置项名称 [→] │ ← pressable, outline -└─────────────────────────────────┘ -``` - -### 卡片组 - -多个卡片垂直排列时,间距 **SHOULD** 为 12px。 diff --git "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Dialog.md" "b/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Dialog.md" deleted file mode 100644 index 5a43e09bc..000000000 --- "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Dialog.md" +++ /dev/null @@ -1,165 +0,0 @@ -# Dialog 对话框 - -> 模态对话框,需要用户响应 - ---- - -## 功能描述 - -在屏幕中央显示的模态对话框,用于需要用户确认或输入的场景。 - ---- - -## 属性规范 - -| 属性 | 类型 | 必需 | 默认值 | 说明 | -|-----|------|-----|-------|------| -| open | boolean | Y | - | 是否打开 | -| onOpenChange | (open) => void | Y | - | 状态变更回调 | -| title | string | Y | - | 标题 | -| description | string | N | - | 描述文本 | -| children | node | N | - | 自定义内容 | -| actions | Action[] | N | - | 操作按钮 | - -### Action 结构 - -``` -Action { - label: string - variant: 'primary' | 'secondary' | 'destructive' - onClick: () => void - loading?: boolean -} -``` - ---- - -## 行为规范 - -### 必须 (MUST) - -1. 显示半透明背景遮罩 -2. 居中显示 -3. 有打开/关闭动画(淡入淡出 + 缩放) -4. 按钮区域固定在底部 - -### 建议 (SHOULD) - -1. 点击遮罩不关闭(需要明确操作) -2. 破坏性操作按钮放在右侧 -3. 取消按钮使用 secondary 变体 - -### 可选 (MAY) - -1. 支持自定义宽度 -2. 支持关闭按钮(X) - ---- - -## 可访问性 - -| 要求 | 说明 | -|-----|------| -| role | alertdialog(警告类)或 dialog(普通) | -| aria-modal | true | -| aria-labelledby | 关联标题 | -| aria-describedby | 关联描述 | -| 焦点陷阱 | 焦点限制在 Dialog 内 | -| ESC 键 | 关闭(如果允许) | - ---- - -## 布局规范 - -``` -┌───────────────────────────────────────┐ -│ [遮罩] │ -│ ┌───────────────────────────────┐ │ -│ │ 标题 │ │ -│ │ 描述 │ │ -│ ├───────────────────────────────┤ │ -│ │ │ │ -│ │ 自定义内容 │ │ -│ │ │ │ -│ ├───────────────────────────────┤ │ -│ │ [取消] [确认] │ │ -│ └───────────────────────────────┘ │ -│ │ -└───────────────────────────────────────┘ -``` - ---- - -## 设计标注 - -### 尺寸规格 - -| 部分 | 规格 | -|-----|------| -| 宽度 | min(90vw, 320px) | -| 圆角 | 16px | -| 内边距 | 24px | -| 按钮间距 | 12px | - -### 动画规格 - -| 属性 | 值 | -|-----|---| -| 时长 | 200ms | -| 缓动 | ease-out | -| 打开 | scale 0.95 → 1, opacity 0 → 1 | -| 关闭 | scale 1 → 0.95, opacity 1 → 0 | - ---- - -## 预设变体 - -### AlertDialog 警告对话框 - -用于危险操作确认: - -``` -┌───────────────────────────────────┐ -│ 确认删除钱包? │ -│ │ -│ 删除后将无法恢复,请确保已备份 │ -│ 助记词。 │ -│ │ -│ [取消] [删除] │ -└───────────────────────────────────┘ -``` - -- title: 必需 -- description: 建议提供 -- actions: 取消 + 危险操作 - -### ConfirmDialog 确认对话框 - -用于普通确认: - -``` -┌───────────────────────────────────┐ -│ 确认发送? │ -│ │ -│ 发送 1.5 ETH 到 │ -│ 0x1234...5678 │ -│ │ -│ [取消] [确认] │ -└───────────────────────────────────┘ -``` - -### PromptDialog 输入对话框 - -用于简单输入: - -``` -┌───────────────────────────────────┐ -│ 重命名钱包 │ -│ │ -│ ┌─────────────────────────────┐ │ -│ │ 新钱包名称 │ │ -│ └─────────────────────────────┘ │ -│ │ -│ [取消] [确认] │ -└───────────────────────────────────┘ -``` diff --git "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Input.md" "b/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Input.md" deleted file mode 100644 index 55e618f56..000000000 --- "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Input.md" +++ /dev/null @@ -1,169 +0,0 @@ -# Input 输入框 - -> 接收用户文本输入 - ---- - -## 功能描述 - -用于接收用户键盘输入的文本框组件。 - ---- - -## 属性规范 - -| 属性 | 类型 | 必需 | 默认值 | 说明 | -|-----|------|-----|-------|------| -| value | string | Y | - | 当前值 | -| onChange | (value) => void | Y | - | 值变更回调 | -| placeholder | string | N | - | 占位提示文本 | -| type | enum | N | 'text' | 输入类型 | -| disabled | boolean | N | false | 禁用状态 | -| readOnly | boolean | N | false | 只读状态 | -| error | string | N | - | 错误信息 | -| maxLength | number | N | - | 最大长度 | -| prefix | node | N | - | 前缀元素 | -| suffix | node | N | - | 后缀元素 | -| clearable | boolean | N | false | 显示清除按钮 | - -### type 类型 - -| 值 | 说明 | 键盘类型 | -|---|------|---------| -| text | 普通文本 | 默认键盘 | -| password | 密码 | 默认键盘 | -| number | 数字 | 数字键盘 | -| decimal | 小数 | 带小数点的数字键盘 | -| tel | 电话 | 电话键盘 | -| email | 邮箱 | 带 @ 的键盘 | - ---- - -## 状态机 - -``` - focus - ┌─────────┐ ──────────► ┌─────────┐ - │ Idle │ │ Focused │ - └─────────┘ ◄────────── └─────────┘ - │ blur │ - │ │ input - │ ▼ - │ ┌─────────┐ - │ │ Dirty │ - │ └─────────┘ - │ │ - │ │ validate - │ ▼ - │ ┌─────────────────────────────────┐ - │ │ │ - │ ▼ ▼ - │ ┌─────────┐ ┌─────────┐ - │ │ Valid │ │ Invalid │ - │ └─────────┘ └─────────┘ - │ - │ disabled=true - ▼ - ┌─────────┐ - │Disabled │ - └─────────┘ -``` - ---- - -## 行为规范 - -### 必须 (MUST) - -1. focus 时显示焦点指示器(边框颜色变化) -2. 有 error 时显示错误样式(红色边框) -3. 有 error 时在输入框下方显示错误信息 -4. disabled 时阻止所有交互 -5. 有关联的 label - -### 建议 (SHOULD) - -1. type="password" 时提供显示/隐藏切换按钮 -2. clearable=true 且有内容时显示清除按钮 -3. 支持 prefix/suffix 自定义元素 -4. 超出 maxLength 时阻止继续输入 - -### 可选 (MAY) - -1. 显示字符计数(当前/最大) -2. 支持自动完成建议 - ---- - -## 可访问性 - -| 要求 | 说明 | -|-----|------| -| label | 必须有关联的 label | -| aria-invalid | error 时设为 true | -| aria-describedby | 关联错误信息元素 | -| aria-disabled | disabled 时设为 true | - ---- - -## 布局规范 - -``` -┌─────────────────────────────────────┐ -│ Label [必填] │ ← 标签行 -├─────────────────────────────────────┤ -│ [前缀] 输入内容... [后缀/清除] │ ← 输入框 -├─────────────────────────────────────┤ -│ ⚠ 错误信息 │ ← 错误提示(可选) -└─────────────────────────────────────┘ -``` - ---- - -## 设计标注 - -### 尺寸规格 - -| 部分 | 规格 | -|-----|------| -| 高度 | 44px | -| 内边距 | 12px 16px | -| 字号 | 16px | -| 圆角 | 8px | -| 边框 | 1px | - -### 颜色规格 - -| 状态 | 边框色 | 背景色 | -|-----|-------|-------| -| default | --color-border | --color-background | -| focused | --color-primary | --color-background | -| error | --color-destructive | --color-destructive/5 | -| disabled | --color-border | --color-muted | - ---- - -## 变体 - -### TextArea 多行文本 - -继承 Input 所有属性,额外支持: - -| 属性 | 类型 | 说明 | -|-----|------|------| -| rows | number | 显示行数 | -| autoResize | boolean | 是否自动调整高度 | -| maxRows | number | 最大行数 | - -### SearchInput 搜索框 - -继承 Input 所有属性,额外支持: - -| 属性 | 类型 | 说明 | -|-----|------|------| -| onSearch | (value) => void | 搜索回调 | -| loading | boolean | 搜索中状态 | - -视觉特征: -- 前缀为搜索图标 -- 圆角更大(pill 形状) diff --git "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Sheet.md" "b/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Sheet.md" deleted file mode 100644 index c64e15e54..000000000 --- "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Sheet.md" +++ /dev/null @@ -1,238 +0,0 @@ -# Sheet 底部抽屉 - -> 从底部滑出的模态面板 - ---- - -## 功能描述 - -从屏幕底部滑出的模态面板,用于展示次级操作或信息,不离开当前页面。 - ---- - -## 属性规范 - -| 属性 | 类型 | 必需 | 默认值 | 说明 | -|-----|------|-----|-------|------| -| open | boolean | Y | - | 是否打开 | -| onOpenChange | (open) => void | Y | - | 状态变更回调 | -| title | string | N | - | 标题 | -| description | string | N | - | 描述文本 | -| children | node | Y | - | 内容 | -| snapPoints | number[] | N | - | 吸附高度点 | -| dismissible | boolean | N | true | 是否可关闭 | - -### snapPoints 吸附点 - -定义 Sheet 可以停留的高度(占屏幕高度的比例): - -``` -snapPoints: [0.25, 0.5, 0.9] -// 25%、50%、90% 三个停留点 -``` - ---- - -## 状态机 - -``` - ┌─────────┐ open=true ┌─────────┐ - │ Closed │ ───────────► │ Opening │ - └─────────┘ └─────────┘ - ▲ │ - │ │ animation done - │ ▼ - │ ┌─────────┐ - │ │ Open │ ◄─┐ - │ └─────────┘ │ snap - │ │ │ - │ ├────────┘ - │ │ - │ close │ swipe down / backdrop / button - │ ▼ - │ ┌─────────┐ - └───────────────── │ Closing │ - └─────────┘ -``` - ---- - -## 行为规范 - -### 必须 (MUST) - -1. 打开时显示半透明背景遮罩 -2. 支持向下滑动关闭(滑动超过 30% 或速度快) -3. 支持点击背景遮罩关闭(dismissible=true 时) -4. 有流畅的打开/关闭动画 -5. 顶部显示拖动指示条 - -### 建议 (SHOULD) - -1. 打开时阻止背景滚动 -2. 使用弹簧动画 -3. 支持多个吸附点 -4. 最大高度不超过 90% 屏幕高度 - -### 可选 (MAY) - -1. 支持嵌套 Sheet -2. 支持左右滑动切换内容 - ---- - -## 可访问性 - -| 要求 | 说明 | -|-----|------| -| role | dialog | -| aria-modal | true | -| aria-labelledby | 关联标题 | -| 焦点陷阱 | 焦点限制在 Sheet 内 | -| 打开时 | 焦点移入 Sheet | -| 关闭时 | 焦点返回触发元素 | -| ESC 键 | 关闭 Sheet | - ---- - -## 布局规范 - -``` -┌─────────────────────────────────────┐ -│ [遮罩层] │ -│ │ -│ ┌─────────────────────────────────┐│ -│ │ ═══════ ││ ← 拖动指示条 -│ │ 标题(可选) ││ -│ │ 描述(可选) ││ -│ ├─────────────────────────────────┤│ -│ │ ││ -│ │ 内容区域 ││ ← 可滚动 -│ │ ││ -│ ├─────────────────────────────────┤│ -│ │ 操作按钮(可选) ││ -│ │ [安全区域] ││ -│ └─────────────────────────────────┘│ -└─────────────────────────────────────┘ -``` - ---- - -## 设计标注 - -### 尺寸规格 - -| 部分 | 规格 | -|-----|------| -| 圆角 | 顶部 24px | -| 拖动条宽度 | 36px | -| 拖动条高度 | 4px | -| 拖动条距顶部 | 8px | -| 内边距 | 16px | -| 安全区域 | 底部 env(safe-area-inset-bottom) | - -### 颜色规格 - -| 部分 | 颜色 | -|-----|------| -| 遮罩 | rgba(0, 0, 0, 0.5) | -| 背景 | --color-background | -| 拖动条 | --color-muted | - -### 动画规格 - -| 属性 | 值 | -|-----|---| -| 打开时长 | 300ms | -| 关闭时长 | 200ms | -| 缓动函数 | spring (damping: 0.8) 或 ease-out | - ---- - -## 手势规范 - -### 下滑关闭 - -``` -滑动距离 < 30% 高度 → 弹回当前位置 -滑动距离 ≥ 30% 高度 → 关闭 -滑动速度 > 500px/s → 无论距离,直接关闭 -``` - -### 多吸附点 - -``` -松手时,根据当前位置和速度方向 -选择最近的吸附点或上/下一个吸附点 -``` - ---- - -## 使用场景 - -| 场景 | 配置 | -|-----|------| -| 确认操作 | 固定高度,有操作按钮 | -| 选择列表 | 可滚动内容,选中后自动关闭 | -| 详情展示 | 多吸附点,可展开查看更多 | -| 表单输入 | 键盘弹出时自动调整高度 | - ---- - -## 技术实现 - -### 必须 (MUST) - -1. **使用 Stackflow 的 BottomSheet 组件作为底层实现** - - ```tsx - import { BottomSheet } from "@stackflow/plugin-basic-ui"; - ``` - -2. 这确保 Sheet 与 Stackflow Activity 系统正确集成,避免页面动画冲突 - -3. **禁止**使用自定义 `position: fixed` 实现,会导致: - - 与 Stackflow 页面转场动画冲突 - - 背景页面"运动"问题 - - 滚动锁定与 Activity 生命周期不同步 - -### 封装规范 - -项目的 `BottomSheet` 组件(`src/components/layout/bottom-sheet.tsx`)应: - -1. 内部使用 `@stackflow/plugin-basic-ui` 的 `BottomSheet` -2. 对外暴露符合本规范的 API -3. 添加项目特定的样式和行为扩展 - -```tsx -// 正确的实现方式 -import { BottomSheet as StackflowBottomSheet } from "@stackflow/plugin-basic-ui"; - -export function BottomSheet({ open, onClose, children, ...props }) { - if (!open) return null; - - return ( - - {/* 自定义样式和内容 */} - {children} - - ); -} -``` - ---- - -## Roadmap / 待办事项 - -| 优先级 | 事项 | 状态 | 说明 | -|--------|------|------|------| -| P1 | 统一 safe-area-inset-bottom 处理 | 待处理 | 确保所有 Sheet Activity 在 iOS 设备上正确处理底部安全区域 | -| P2 | Sheet 手势下滑关闭 | 待评估 | Stackflow BottomSheet 是否原生支持,或需要额外实现 | -| P2 | Sheet 多吸附点支持 | 待评估 | 评估是否需要支持多个高度吸附点 | - ---- - -## 参考文档 - -- **Stackflow 官方文档(LLM 友好版)**: https://stackflow.so/llms-full.txt -- **`@stackflow/plugin-basic-ui` API**: 包含 `AppScreen`、`BottomSheet`、`Modal` 三种 Activity 容器组件 diff --git "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Toast.md" "b/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Toast.md" deleted file mode 100644 index 1bc7ea394..000000000 --- "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/Toast.md" +++ /dev/null @@ -1,172 +0,0 @@ -# Toast 轻提示 - -> 轻量级反馈消息 - ---- - -## 功能描述 - -短暂显示的轻量级消息提示,用于操作反馈,不需要用户交互。 - ---- - -## 属性规范 - -| 属性 | 类型 | 必需 | 默认值 | 说明 | -|-----|------|-----|-------|------| -| message | string | Y | - | 提示内容 | -| type | enum | N | 'info' | 类型 | -| duration | number | N | 3000 | 显示时长(ms) | -| position | enum | N | 'top' | 显示位置 | -| action | Action | N | - | 操作按钮 | - -### type 类型 - -| 值 | 图标 | 颜色 | 使用场景 | -|---|------|-----|---------| -| info | ℹ | 中性 | 普通信息 | -| success | ✓ | 绿色 | 操作成功 | -| warning | ⚠ | 黄色 | 警告提示 | -| error | ✗ | 红色 | 操作失败 | - -### position 位置 - -| 值 | 说明 | -|---|------| -| top | 顶部居中 | -| bottom | 底部居中(避开 TabBar) | - ---- - -## 行为规范 - -### 必须 (MUST) - -1. 自动消失(duration 后) -2. 有进入/退出动画 -3. 多个 Toast 可堆叠显示 -4. 同时最多显示 3 个 - -### 建议 (SHOULD) - -1. 支持滑动提前关闭 -2. 新 Toast 从最近位置进入 -3. 点击 action 后自动关闭 - -### 可选 (MAY) - -1. 支持 duration: Infinity(持续显示) -2. 支持进度条显示剩余时间 - ---- - -## 可访问性 - -| 要求 | 说明 | -|-----|------| -| role | status(info)或 alert(error/warning) | -| aria-live | polite(info)或 assertive(error) | -| 不阻断 | 不阻止用户操作 | - ---- - -## 布局规范 - -### 单条 Toast - -``` -┌───────────────────────────────────┐ -│ [图标] 提示消息内容 [操作] │ -└───────────────────────────────────┘ -``` - -### 多条堆叠 - -``` -┌───────────────────────────────────┐ -│ Toast 3 (最新) │ -└───────────────────────────────────┘ - ↑ 8px 间距 -┌───────────────────────────────────┐ -│ Toast 2 │ -└───────────────────────────────────┘ - ↑ 8px 间距 -┌───────────────────────────────────┐ -│ Toast 1 (最旧) │ -└───────────────────────────────────┘ -``` - ---- - -## 设计标注 - -### 尺寸规格 - -| 部分 | 规格 | -|-----|------| -| 最小宽度 | 200px | -| 最大宽度 | min(90vw, 400px) | -| 内边距 | 12px 16px | -| 圆角 | 8px | -| 图标尺寸 | 20px | -| 与边缘距离 | 16px | -| 与安全区距离 | env(safe-area-inset-*) | - -### 动画规格 - -| 属性 | 值 | -|-----|---| -| 进入 | slideIn + fadeIn, 200ms | -| 退出 | slideOut + fadeOut, 150ms | -| 缓动 | ease-out | - -### 默认持续时间 - -| 类型 | 时长 | -|-----|------| -| success | 2000ms | -| info | 3000ms | -| warning | 4000ms | -| error | 5000ms | - ---- - -## 使用场景 - -### 操作成功 - -``` -type: success -message: "复制成功" -duration: 2000 -``` - -### 操作失败 - -``` -type: error -message: "网络连接失败" -action: { label: "重试", onClick: retry } -duration: 5000 -``` - -### 带操作 - -``` -type: info -message: "交易已提交" -action: { label: "查看详情", onClick: viewTx } -``` - ---- - -## API 调用方式 - -建议提供命令式 API: - -``` -toast.success("复制成功") -toast.error("操作失败", { action: { label: "重试", onClick } }) -toast.info("提示信息") -toast.warning("警告信息") -``` diff --git "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/index.md" "b/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/index.md" deleted file mode 100644 index 0d2bde9ce..000000000 --- "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/01-\345\237\272\347\241\200\347\273\204\344\273\266/index.md" +++ /dev/null @@ -1,60 +0,0 @@ -# 基础组件 - -> 无业务逻辑的通用 UI 组件 - ---- - -## 概述 - -基础组件是构建应用的最底层 UI 单元,不包含任何业务逻辑。 - ---- - -## 组件列表 - -| 组件 | 说明 | 优先级 | -|-----|------|-------| -| [Button](./Button) | 按钮,触发动作 | P0 | -| [Input](./Input) | 文本输入框 | P0 | -| [Card](./Card) | 卡片容器 | P0 | -| [Sheet](./Sheet) | 底部抽屉 | P0 | -| [Dialog](./Dialog) | 模态对话框 | P0 | -| [Toast](./Toast) | 轻提示消息 | P0 | -| Skeleton | 骨架屏 | P1 | -| Avatar | 头像 | P1 | -| Badge | 徽标 | P2 | -| Switch | 开关 | P1 | -| Checkbox | 复选框 | P2 | -| Radio | 单选框 | P2 | - ---- - -## 通用规范 - -### 尺寸变体 - -所有支持尺寸的组件 **SHOULD** 提供以下变体: - -| 变体 | 说明 | -|-----|------| -| sm | 紧凑型,适用于密集布局 | -| md | 默认尺寸 | -| lg | 大尺寸,适用于突出显示 | - -### 状态变体 - -交互组件 **MUST** 支持以下状态: - -| 状态 | 说明 | -|-----|------| -| default | 默认状态 | -| hover | 悬停状态(桌面端) | -| pressed | 按压状态 | -| focused | 聚焦状态 | -| disabled | 禁用状态 | - -### 主题适配 - -- **MUST** 支持浅色/深色主题 -- **MUST** 使用语义化颜色 token -- **MUST NOT** 硬编码颜色值 diff --git "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/AddressDisplay.md" "b/docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/AddressDisplay.md" deleted file mode 100644 index 7985d295d..000000000 --- "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/AddressDisplay.md" +++ /dev/null @@ -1,118 +0,0 @@ -# AddressDisplay 地址显示 - -> 显示区块链地址,支持复制 - ---- - -## 功能描述 - -格式化显示区块链地址,支持缩略显示和一键复制。 - ---- - -## 属性规范 - -| 属性 | 类型 | 必需 | 默认值 | 说明 | -|-----|------|-----|-------|------| -| address | string | Y | - | 完整地址 | -| format | enum | N | 'short' | 显示格式 | -| copyable | boolean | N | true | 是否可复制 | -| showQR | boolean | N | false | 是否显示二维码按钮 | -| onCopy | () => void | N | - | 复制成功回调 | -| onQRPress | () => void | N | - | 二维码按钮点击回调 | - -### format 格式 - -| 值 | 说明 | 示例 | -|---|------|------| -| short | 首尾缩略 | 0x1234...5678 | -| medium | 中等长度 | 0x123456...345678 | -| full | 完整显示 | 0x1234567890abcdef... | - ---- - -## 显示规则 - -### 缩略算法 - -``` -short: 前6位 + "..." + 后4位 -medium: 前8位 + "..." + 后6位 -full: 完整显示,必要时换行 -``` - -### 样式要求 - -- **MUST** 使用等宽字体 -- **SHOULD** 字母小写显示(统一风格) -- **MAY** 支持 checksum 高亮(EVM 地址大小写混合) - ---- - -## 行为规范 - -### 必须 (MUST) - -1. copyable=true 时,点击地址复制完整地址 -2. 复制成功后显示反馈(Toast 或图标变化) -3. 使用等宽字体 - -### 建议 (SHOULD) - -1. 复制时提供触觉反馈 -2. 显示复制图标作为视觉提示 -3. 复制后图标短暂变为勾选状态 - -### 可选 (MAY) - -1. 长按显示完整地址 -2. 支持选择文本 - ---- - -## 布局规范 - -### 标准布局 - -``` -┌─────────────────────────────────────┐ -│ 0x1234...5678 [复制] [二维码] │ -└─────────────────────────────────────┘ -``` - -### 行内布局 - -``` -地址:0x1234...5678 📋 -``` - ---- - -## 设计标注 - -| 部分 | 规格 | -|-----|------| -| 字体 | 等宽字体 | -| 字号 | 14px | -| 图标尺寸 | 16px | -| 图标间距 | 8px | - ---- - -## 复制反馈 - -### 图标状态 - -``` -默认: 📋 (复制图标) -点击后: ✓ (勾选图标,持续 1.5s) -恢复: 📋 -``` - -### Toast 反馈 - -``` -type: success -message: "地址已复制" -duration: 2000 -``` diff --git "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/AmountDisplay.md" "b/docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/AmountDisplay.md" deleted file mode 100644 index 7f1c0a54d..000000000 --- "a/docs/white-book/05-\347\273\204\344\273\266\347\257\207/02-\351\200\232\347\224\250\347\273\204\344\273\266/AmountDisplay.md" +++ /dev/null @@ -1,262 +0,0 @@ -# AmountDisplay 金额显示 - -> 格式化显示加密货币金额,支持平滑数字动画 - ---- - -## 功能描述 - -将原始金额值格式化为人类可读的形式,支持多种显示配置。使用 `@number-flow/react` 实现专业的数字动画效果。 - -### 核心特性 - -- **Amount 类型支持**: 直接接收 `Amount` 对象,自动提取元数据 -- **数字动画**: 数值变化时逐位平滑过渡动画 -- **加载状态**: 显示 "0" 配合呼吸动画,数据到达后平滑过渡到真实值 -- **完整可访问性**: 屏幕阅读器正确读取数值 -- **等宽字体**: 使用 `font-mono` 确保数字宽度一致 - ---- - -## 与 Amount 类型集成 - -推荐使用 `Amount` 对象作为 value 属性,组件会自动提取 decimals 和 symbol: - -```tsx -import { Amount } from '@/types/amount' -import { AmountDisplay } from '@/components/amount-display' - -const balance = Amount.fromRaw('1500000000000000000', 18, 'ETH') - -// 使用 Amount 对象(推荐) - -// 自动显示: 1.5 ETH - -// 也支持直接使用 number - -``` - -更多关于 Amount 类型的信息,请参阅 [Amount 类型系统](../../03-架构篇/08-Amount类型系统/index.md)。 - ---- - -## 属性规范 - -| 属性 | 类型 | 必需 | 默认值 | 说明 | -|-----|------|-----|-------|------| -| value | number \| bigint \| string \| Amount | Y | - | 金额值(推荐使用 Amount) | -| symbol | string | N | - | 货币符号(使用 Amount 时自动提取) | -| decimals | number | N | 8 | 小数位数(使用 Amount 时自动提取) | -| sign | enum | N | 'never' | 符号显示模式 | -| color | enum | N | 'default' | 颜色模式 | -| size | enum | N | 'md' | 尺寸 | -| weight | enum | N | 'normal' | 字重 | -| loading | boolean | N | false | 是否加载中 | -| animated | boolean | N | true | 是否启用动画 | -| hidden | boolean | N | false | 隐私模式(显示 ••••••) | -| compact | boolean | N | false | 紧凑模式(1K, 1M) | -| mono | boolean | N | true | 是否等宽字体 | - -### sign 符号模式 - -| 值 | 说明 | 示例 | -|---|------|------| -| always | 始终显示 +/- | +100, -50 | -| negative | 仅负数显示 - | 100, -50 | -| never | 不显示符号 | 100, 50 | - -### color 颜色模式 - -| 值 | 说明 | -|---|------| -| default | 默认颜色 | -| auto | 正数绿色,负数红色 | -| positive | 强制绿色 | -| negative | 强制红色 | - -### weight 字重 - -| 值 | 说明 | -|---|------| -| normal | 正常字重 | -| medium | 中等 | -| semibold | 半粗 | -| bold | 粗体 | - ---- - -## 格式化规则 - -### 小数位处理 - -``` -输入: 1000000000 (decimals: 8) -输出: 10.00000000 - -输入: 123456789 (decimals: 8) -输出: 1.23456789 - -输入: 1234567890123456789n (decimals: 18) -输出: 1.234567890123456789 -``` - -### displayDecimals 显示精度 - -``` -value: 1.23456789 -displayDecimals: 4 -输出: 1.2346 (四舍五入) - -displayDecimals: auto -小数 < 0.0001 → 显示全部有效数字 -其他 → 最多显示 8 位 -``` - -### 千分位分隔 - -``` -1234567.89 → 1,234,567.89 -``` - -### 极端值处理 - -``` -value: 0 -输出: 0 SYMBOL - -value: 0.000000001 (极小值) -输出: < 0.00000001 SYMBOL - -value: 999999999999 (极大值) -输出: 999.99B SYMBOL (使用 K/M/B 后缀) -``` - ---- - -## 行为规范 - -### 必须 (MUST) - -1. 使用千分位分隔符 -2. 金额使用等宽数字字体(`font-mono`) -3. 正确处理 bigint 类型 -4. **可访问性**: 必须提供 `aria-label` 供屏幕阅读器读取 -5. **加载状态**: 显示 "0" 配合呼吸动画(`animate-pulse`),不使用色块骨架屏 - -### 建议 (SHOULD) - -1. 金额右对齐 -2. 符号跟随金额,不换行 -3. 使用 `@number-flow/react` 实现数字动画 -4. 数值变化时平滑过渡,而非直接跳变 - -### 可选 (MAY) - -1. 支持点击复制原始值 -2. 支持显示法币估值 - ---- - -## 动画规范 - -### 数字动画 - -使用 `@number-flow/react` 库实现专业的数字动画: - -```tsx -import NumberFlow from '@number-flow/react' - - -``` - -### 加载状态动画 - -加载时显示 "0" 配合呼吸动画,数据到达后 NumberFlow 会自动从 0 平滑过渡到真实值: - -```tsx -// 加载中 - - - -// 数据到达后 -