-
Notifications
You must be signed in to change notification settings - Fork 3
Wallet interface #134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Wallet interface #134
Changes from all commits
1020f28
2c9c78c
bcc503a
610a5e6
d65d3de
e02dc68
b2ad177
0659823
13540e8
ae830e8
ae29d9f
dbda488
3731128
353d1f0
a48eac4
806441e
db65476
4997ead
8137b73
41abf45
fea2619
d94e931
602f9d0
8b8c9b0
b700dbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| .dart_tool/ | ||
| build/ | ||
| coverage/ | ||
| .idea/ | ||
|
|
||
| .env* | ||
|
|
||
| protos/ | ||
| coverage/ | ||
|
|
||
| lib/src/crypto/tron/repositories/rpc/api/ |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| # Repository Guidelines | ||
|
|
||
| ## Project Structure & Module Organization | ||
| - `lib/`: Package source entry `walletkit_dart.dart` plus modules under `src/` (EVM, UTXO, Tron, wallet utils, domain entities, utils). | ||
| - `test/`: Organized by domain (e.g., `test/ci/evm`, `test/ci/fetching`, `test/ci/tron`). `ci-mocked/` holds tests with mocks; `no_ci/` are local-only. | ||
| - `example/`: Minimal CLI helpers and usage snippets. | ||
| - `protos/`: Protocol definitions; generated Dart files live in `lib/src/crypto/tron/repositories/rpc/`. | ||
| - `coverage/`: Local/CI coverage outputs. | ||
|
|
||
| ## Build, Test, and Development Commands | ||
| - Prereqs: Dart SDK `^3.7.2` (see `pubspec.yaml`). | ||
| - Install deps: `dart pub get` | ||
| - Static analysis: `dart analyze` | ||
| - Format code: `dart format .` | ||
| - Run all tests: `dart test` | ||
| - Run a test subset: `dart test test/ci/evm` | ||
| - Generate coverage (local): | ||
| - `dart pub global activate coverage` | ||
| - `dart test --coverage=coverage` | ||
| - `dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --package=. --report-on=lib` | ||
|
|
||
| ## Coding Style & Naming Conventions | ||
| - Style: Dart `package:lints/recommended` (see `analysis_options.yml`). | ||
| - Indentation: 2 spaces; max line length per Dart defaults. | ||
| - Naming: types in `UpperCamelCase`, methods/vars in `lowerCamelCase`, files in `lower_snake_case.dart`. | ||
| - Keep public API exports centralized in `lib/walletkit_dart.dart`. | ||
|
|
||
| ## Testing Guidelines | ||
| - Framework: `package:test` with domain-focused directories. | ||
| - Name tests after subject and behavior: `feature_action_test.dart`. | ||
| - Prefer deterministic tests; use `ci-mocked` for network-sensitive paths. | ||
| - Env for integration tests: uses `.env` via `dotenv`. Common keys: `ETHERSCAN_API_KEYS`, `TRONSCAN_API_KEYS`, `DEV_SEED`, `REJECT_SEED`, `TRON_SEED`. Do not commit secrets. | ||
|
|
||
| ## Commit & Pull Request Guidelines | ||
| - Commits: prefer Conventional Commits (`feat:`, `fix:`, `refactor:`, `test:`, `chore:`). Keep changes scoped and descriptive. | ||
| - PRs should include: | ||
| - Purpose and summary of changes; link related issues. | ||
| - Notes on API surface changes (exports in `walletkit_dart.dart`). | ||
| - Test coverage: list added/updated tests and how to run a focused subset. | ||
| - Screenshots/trace snippets when debugging protocol/tx parsing. | ||
|
|
||
| ## Security & Configuration Tips | ||
| - Secrets: store locally in `.env`; CI uses repository secrets. Never log private keys or seeds. | ||
| - When adding new RPC/backends, gate keys via `dotenv` and add mocked tests under `test/ci-mocked`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| MIT License | ||
|
|
||
| Copyright (c) 2024 NOMO | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,154 +1,104 @@ | ||
| [](https://github.com/nomo-app/walletkit-dart/actions/workflows/dart.yml) | ||
| [](https://github.com/nomo-app/walletkit-dart/actions/workflows/dart.yml) | ||
| [](https://dev.nomo.app/walletkit-dart/coverage/) | ||
|
|
||
| # WalletKit-Dart | ||
|
|
||
| WalletKit-Dart provides features for interacting with | ||
| Bitcoin/Ethereum/Tron/ZENIQ/ZENIQ Smartchain/Polygon/Binance | ||
| Smartchain/Litecoin/Bitcoin Cash/Eurocoin as well as several layer 2 chains. If | ||
| needed, it is easy to expand WalletKit-Dart with other chains. | ||
|
|
||
| See the [Uniswap WebOn](https://github.com/nomo-app/uniswap-webon) or the | ||
| Unit-tests for a practical example how to use WalletKit-Dart. | ||
|
|
||
| See the [api-docs](https://dev.nomo.app/walletkit-dart) for a list of individual | ||
| functions. | ||
| A powerful Dart library for interacting with multiple blockchain networks, including Bitcoin, Ethereum, Tron, ZENIQ, Polygon, Binance Smart Chain, Litecoin, Bitcoin Cash, and Eurocoin, as well as various layer 2 chains. | ||
|
|
||
| ## Features | ||
|
|
||
| - Sending transactions, including legacy/SegWit/P2SH/EVM | ||
| - Fetching transactions and balances, including xPub/zPub/ERC20 | ||
| - Advanced parsing for making smart contract calls readable for humans (EVM) | ||
| - Deriving addresses from mnemonics (HD-Wallet) | ||
| - Performance by default: Immutable data will be cached in-memory | ||
| - A large suite of Unit-tests | ||
|
|
||
| ## Why WalletKit-Dart? | ||
|
|
||
| WalletKit-Dart has been inspired by the | ||
| [WalletKit-C](https://github.com/blockset-corp/walletkit) from blockset-corp and | ||
| by [bitcoin_flutter](https://github.com/dart-bitcoin/bitcoin_flutter) from the | ||
| dart-bitcoin-project. | ||
|
|
||
| The WalletKit-C was one of the first WalletKits that combined UTXO-chains and | ||
| EVM-chains under a unified class hierarchy. However, the WalletKit-C was plagued | ||
| by race conditions and memory corruptions. Also, the WalletKit-C had an "object | ||
| oriented architecture" that was poorly supported by the C-language. | ||
|
|
||
| bitcoin_flutter worked well, but bitcoin_flutter did not support modern | ||
| null-safe Dart and it was difficult to expand for multiple chains. | ||
|
|
||
| WalletKit-Dart has been developed to solve all those problems. WalletKit-Dart | ||
| works with modern Dart-versions and is easy to expand for multiple chains. | ||
|
|
||
| ## How to integrate | ||
|
|
||
| First, add this package as a Git-submodule by using Git-commands: | ||
|
|
||
| ``` | ||
| - **Multi-Chain Support**: Native support for both UTXO-based and EVM-based chains | ||
| - **Transaction Management**: | ||
| - Send transactions (legacy/SegWit/P2SH/EVM) | ||
| - Fetch transactions and balances (including xPub/zPub/ERC20) | ||
| - Advanced smart contract call parsing for human readability | ||
| - **Wallet Features**: | ||
| - HD-Wallet support with mnemonic phrase derivation | ||
| - Address validation and management | ||
| - Fee estimation | ||
| - **Performance Optimized**: | ||
| - In-memory caching for immutable data | ||
| - Stateless API design | ||
| - **Comprehensive Testing**: | ||
| - Extensive unit test suite | ||
| - CI/CD integration | ||
|
|
||
| ## Getting Started | ||
|
|
||
| ### Installation | ||
|
|
||
| Add WalletKit-Dart to your project as a Git submodule: | ||
|
|
||
| ```bash | ||
| git submodule add https://github.com/nomo-app/walletkit-dart.git packages/walletkit-dart | ||
| ``` | ||
|
|
||
| Next, expand your pubspec.yaml accordingly: | ||
| Update your `pubspec.yaml`: | ||
|
|
||
| ``` | ||
| ```yaml | ||
| dependencies: | ||
| walletkit_dart: | ||
| path: packages/walletkit-dart | ||
| walletkit_dart: | ||
| path: packages/walletkit-dart | ||
| ``` | ||
|
|
||
| Afterwards, clone submodules with: | ||
| Initialize the submodules: | ||
|
|
||
| ``` | ||
| ```bash | ||
| git submodule update --init --recursive | ||
| ``` | ||
|
|
||
| The `--recursive` is important because WalletKit-Dart depends on other | ||
| grandchild-submodules. | ||
| ### Requirements | ||
|
|
||
| ## Architecture | ||
|
|
||
| WalletKit-Dart is built for both _UTXO-chains_ and _EVM-chains_. WalletKit-Dart | ||
| provides a class hierarchy where both `UTXOTransaction` and `EVMTransaction` | ||
| inherit from a `GenericTransaction` base class. | ||
|
|
||
| Here is a quick summary if you do not yet understand the difference between UTXO | ||
| and EVM: | ||
|
|
||
| ``` | ||
| UTXO-based chains and EVM-based chains are two different architectures for blockchain systems. | ||
| UTXO (Unspent Transaction Output) is a concept used in Bitcoin and some other cryptocurrencies. | ||
| In this architecture, each transaction spends one or more previously unspent outputs and creates new outputs. | ||
| The sum of the inputs must be equal to or greater than the sum of the outputs. | ||
| EVM (Ethereum Virtual Machine) is used in Ethereum and some other blockchain platforms that support smart contracts. | ||
| In this architecture, each transaction is executed in the EVM as a smart contract, which can modify the state of the blockchain and create new contracts. | ||
| The state is stored in a database, rather than as a set of unspent outputs. | ||
| In summary, UTXO-based chains focus on tracking ownership of tokens, while EVM-based chains focus on executing arbitrary code in the form of smart contracts. | ||
| ``` | ||
|
|
||
| ## API Philosophy | ||
| - Dart SDK: ^3.7.2 | ||
| - See [pubspec.yaml](pubspec.yaml) for detailed dependencies | ||
|
|
||
| WalletKit-Dart provides a _stateless API_. Neither does it store seed phrases, | ||
| nor does it store transactions. It is the users responsibility to store any | ||
| needed data. | ||
|
|
||
| By design, WalletKit-Dart does not have any persistent databases. Instead, | ||
| WalletKit-Dart only has a few in-memory-caches to improve performance of | ||
| repeated calls. This design helps to simplify the API. | ||
|
|
||
| Moreover, WalletKit-Dart provides different APIs for UTXO-chains and EVM-chains. | ||
| Although every transaction inherits from a generic base transaction, we want to | ||
| provide APIs that are specifically targeted for the architecture of a chain. In | ||
| that sense, we deviate from a traditional object oriented approach. | ||
|
|
||
| In other words, while we aim to reuse code between chains, we do not want to | ||
| create broken abstraction layers by abstracting too much complexity away. | ||
|
|
||
| ## Backend APIs | ||
|
|
||
| Depending on the chain, WalletKit-Dart depends on multiple backend APIs. For | ||
| UTXO-chains, WalletKit-Dart depends on the | ||
| [ElectrumX Protocol](https://electrumx.readthedocs.io/en/latest/protocol-methods.html). | ||
| ## Architecture | ||
|
|
||
| For EVM-chains, WalletKit-Dart depends on several JSON-RPC-providers as well as | ||
| etherscan-styled APIs. | ||
| WalletKit-Dart is designed to handle both UTXO-based and EVM-based chains efficiently: | ||
|
|
||
| ## The following Unit-tests show how to use WalletKit-Dart | ||
| ### UTXO Chains | ||
| - Bitcoin and similar cryptocurrencies | ||
| - Tracks ownership through unspent transaction outputs | ||
| - Efficient for simple value transfers | ||
|
|
||
| [Get Token Info for a ERC20 Token](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/evm/erc20_test.dart) | ||
| ### EVM Chains | ||
| - Ethereum and compatible networks | ||
| - Supports smart contracts and complex transactions | ||
| - State-based architecture | ||
|
|
||
| [Fetch EVM Transaction from Explorer](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/evm/evm_explorer_test.dart) | ||
| ## API Design Philosophy | ||
|
|
||
| [Walletkit Json RPC Interface](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/evm/evm_rcp_test.dart) | ||
| - **Stateless**: No storage of seed phrases or transactions | ||
| - **Memory-Efficient**: Uses in-memory caches for performance | ||
| - **Chain-Specific APIs**: Specialized interfaces for different chain architectures | ||
| - **No Broken Abstractions**: Maintains clear separation between chain types | ||
|
|
||
| [Fetch Bitcoin Transactions](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/fetching/assets/bitcoin_fetch_test.dart) | ||
| ## Backend Integration | ||
|
|
||
| [Get best health endpoints utxo](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/fetching/endpoint_test.dart) | ||
| - **UTXO Chains**: Uses the [ElectrumX Protocol](https://electrumx.readthedocs.io/en/latest/protocol-methods.html) | ||
| - **EVM Chains**: Integrates with JSON-RPC providers and Etherscan-style APIs | ||
|
|
||
| [EPubKey Derivation](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/fetching/epubkey_test.dart) | ||
| ## Examples | ||
|
|
||
| [Fetch UTXO Transaction](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/fetching/fetch_utxo_transactions_test.dart) | ||
| Check out these example implementations: | ||
|
|
||
| [Parse MessageHex into EVM Transaction](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/parsing/parse_hex_transaction_test.dart) | ||
| - [Uniswap WebOn](https://github.com/nomo-app/uniswap-webon) | ||
| - [API Documentation](https://dev.nomo.app/walletkit-dart) | ||
|
|
||
| [Decode datafield from EVM Transaction](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/parsing/reverse-hash-computation_test.dart) | ||
| ### Test Examples | ||
|
|
||
| [Address Validation](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/sending/address_validation_test.dart) | ||
| The test suite provides practical examples for various use cases: | ||
|
|
||
| [Estimate Fee](https://github.com/nomo-app/walletkit-dart/blob/main/test/ci/gasfees_test.dart) | ||
| - [ERC20 Token Info](test/ci/evm/erc20_test.dart) | ||
| - [EVM Transaction Fetching](test/ci/evm/evm_explorer_test.dart) | ||
| - [Bitcoin Transaction Management](test/ci/fetching/assets/bitcoin_fetch_test.dart) | ||
| - [Address Validation](test/ci/sending/address_validation_test.dart) | ||
| - [Gas Fee Estimation](test/ci/gasfees_test.dart) | ||
|
|
||
| [Send EVM Transaction](https://github.com/nomo-app/walletkit-dart/blob/main/test/no_ci/send_evm_test.dart) | ||
| ## Contributing | ||
|
|
||
| [Fetch Peers from ElectrumX](https://github.com/nomo-app/walletkit-dart/blob/main/test/no_ci/peers_test.dart) | ||
| Contributions are welcome! Please feel free to submit a Pull Request. | ||
|
|
||
| [Broadcast UTXO Transaction](https://github.com/nomo-app/walletkit-dart/blob/main/test/no_ci/wallet_test.dart) | ||
| ## License | ||
|
|
||
| protoc -I. | ||
| --dart_out=grpc:/home/thomas/src/walletkit-dart/lib/src/crypto/tron/rpc | ||
| core/Tron.proto core/Discover.proto core/TronInventoryItems.proto | ||
| core/contract/common.proto core/contract/account_contract.proto | ||
| core/contract/asset_issue_contract.proto core/contract/balance_contract.proto | ||
| core/contract/exchange_contract.proto core/contract/market_contract.proto | ||
| core/contract/proposal_contract.proto core/contract/shield_contract.proto | ||
| core/contract/smart_contract.proto core/contract/storage_contract.proto | ||
| core/contract/witness_contract.proto api/api.proto | ||
| This project is licensed under the MIT License - see the LICENSE file for details. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import 'dart:async'; | ||
|
|
||
| abstract class Command<T> { | ||
| String get name; | ||
| String get description; | ||
| FutureOr<T> execute(List<String> args); | ||
| } | ||
|
|
||
| class CommandResult<T> { | ||
| final bool success; | ||
| final T? data; | ||
| final String? error; | ||
|
|
||
| CommandResult.success(this.data) | ||
| : success = true, | ||
| error = null; | ||
| CommandResult.failure(this.error) | ||
| : success = false, | ||
| data = null; | ||
|
|
||
| @override | ||
| String toString() { | ||
| if (success) { | ||
| return data?.toString() ?? 'Command completed successfully'; | ||
| } else { | ||
| return 'Error: ${error ?? "Unknown error"}'; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| class CommandRegistry { | ||
| final Map<String, Command> _commands = {}; | ||
|
|
||
| void register(Command command) { | ||
| _commands[command.name.toLowerCase()] = command; | ||
| } | ||
|
|
||
| Command? get(String name) { | ||
| return _commands[name.toLowerCase()]; | ||
| } | ||
|
|
||
| List<Command> get commands => _commands.values.toList(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,38 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'dart:io'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'command.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class HelpCommand extends Command<String> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final CommandRegistry registry; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| HelpCommand(this.registry); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get name => 'help'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get description => 'Show available commands'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String execute(List<String> args) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final buffer = StringBuffer('Available commands:\n'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (final command in registry.commands) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| buffer.writeln(' ${command.name.padRight(10)} - ${command.description}'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return buffer.toString(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class ExitCommand extends Command<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get name => 'exit'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String get description => 'Exit the application'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Future<void> execute(List<String> args) async { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print('Goodbye!'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+26
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Implement graceful shutdown. The @override
Future<void> execute(List<String> args) async {
print('Goodbye!');
- exit(0);
+ // Signal the application to start cleanup
+ return Future.value();
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add duplicate command name validation.
The
registermethod should check for existing commands with the same name (case-insensitive) to prevent overwriting.void register(Command command) { + ArgumentError.checkNotNull(command, 'command'); + final normalizedName = command.name.toLowerCase(); + if (_commands.containsKey(normalizedName)) { + throw ArgumentError('Command "${command.name}" is already registered'); + } - _commands[command.name.toLowerCase()] = command; + _commands[normalizedName] = command; }📝 Committable suggestion