-
Notifications
You must be signed in to change notification settings - Fork 238
feat(kyc): Message signing #3161
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: dev
Are you sure you want to change the base?
Changes from all commits
5ba6ba1
77b2bdc
c79f834
7238db8
dfe8b41
e95b273
b63f600
0705000
246c57c
0cd55db
3e56023
f123da5
9b50c3f
60b5b45
7fc2867
2dadb7b
03e303d
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,82 @@ | ||||||||||||||||
| --- | ||||||||||||||||
| title: Naming Conventions | ||||||||||||||||
| description: Overview of the recommended naming conventions when using bloc. | ||||||||||||||||
| --- | ||||||||||||||||
|
|
||||||||||||||||
| import EventExamplesGood1 from '~/components/naming-conventions/EventExamplesGood1Snippet.astro'; | ||||||||||||||||
| import EventExamplesBad1 from '~/components/naming-conventions/EventExamplesBad1Snippet.astro'; | ||||||||||||||||
| import StateExamplesGood1Snippet from '~/components/naming-conventions/StateExamplesGood1Snippet.astro'; | ||||||||||||||||
| import SingleStateExamplesGood1Snippet from '~/components/naming-conventions/SingleStateExamplesGood1Snippet.astro'; | ||||||||||||||||
| import StateExamplesBad1Snippet from '~/components/naming-conventions/StateExamplesBad1Snippet.astro'; | ||||||||||||||||
|
|
||||||||||||||||
| The following naming conventions are simply recommendations and are completely optional. Feel free to use whatever naming conventions you prefer. You may find some of the examples/documentation do not follow the naming conventions mainly for simplicity/conciseness. These conventions are strongly recommended for large projects with multiple developers. | ||||||||||||||||
|
|
||||||||||||||||
| ## Event Conventions | ||||||||||||||||
|
|
||||||||||||||||
| Events should be named in the **past tense** because events are things that have already occurred from the bloc's perspective. | ||||||||||||||||
|
|
||||||||||||||||
| ### Anatomy | ||||||||||||||||
|
|
||||||||||||||||
| `BlocSubject` + `Noun (optional)` + `Verb (event)` | ||||||||||||||||
|
|
||||||||||||||||
| Initial load events should follow the convention: `BlocSubject` + `Started` | ||||||||||||||||
|
|
||||||||||||||||
| :::note | ||||||||||||||||
| The base event class should be name: `BlocSubject` + `Event`. | ||||||||||||||||
| ::: | ||||||||||||||||
|
|
||||||||||||||||
| ### Examples | ||||||||||||||||
|
|
||||||||||||||||
| ✅ **Good** | ||||||||||||||||
|
|
||||||||||||||||
| <EventExamplesGood1 /> | ||||||||||||||||
|
|
||||||||||||||||
| ❌ **Bad** | ||||||||||||||||
|
|
||||||||||||||||
| <EventExamplesBad1 /> | ||||||||||||||||
|
|
||||||||||||||||
| ## State Conventions | ||||||||||||||||
|
|
||||||||||||||||
| States should be nouns because a state is just a snapshot at a particular point in time. There are two common ways to represent state: using subclasses or using a single class. | ||||||||||||||||
|
|
||||||||||||||||
| ### Anatomy | ||||||||||||||||
|
|
||||||||||||||||
| #### Subclasses | ||||||||||||||||
|
|
||||||||||||||||
| `BlocSubject` + `Verb (action)` + `State` | ||||||||||||||||
|
|
||||||||||||||||
| When representing the state as multiple subclasses `State` should be one of the following: | ||||||||||||||||
|
|
||||||||||||||||
| `Initial` | `Success` | `Failure` | `InProgress` | ||||||||||||||||
|
|
||||||||||||||||
| :::note | ||||||||||||||||
| Initial states should follow the convention: `BlocSubject` + `Initial`. | ||||||||||||||||
| ::: | ||||||||||||||||
|
|
||||||||||||||||
| #### Single Class | ||||||||||||||||
|
|
||||||||||||||||
| `BlocSubject` + `State` | ||||||||||||||||
|
|
||||||||||||||||
| When representing the state as a single base class an enum named `BlocSubject` + `Status` should be used to represent the status of the state: | ||||||||||||||||
|
|
||||||||||||||||
| `initial` | `success` | `failure` | `loading`. | ||||||||||||||||
|
|
||||||||||||||||
| :::note | ||||||||||||||||
| The base state class should always be named: `BlocSubject` + `State`. | ||||||||||||||||
| ::: | ||||||||||||||||
|
|
||||||||||||||||
| ### Examples | ||||||||||||||||
|
|
||||||||||||||||
| ✅ **Good** | ||||||||||||||||
|
|
||||||||||||||||
| ##### Subclasses | ||||||||||||||||
|
Contributor
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. Fix heading level jump. The heading on line 72 jumps from h3 to h5, skipping h4. This violates markdown best practices and can cause issues with document structure and accessibility. Apply this diff to correct the heading level: -##### Subclasses
+#### SubclassesAnd also fix line 76: -##### Single Class
+#### Single Class📝 Committable suggestion
Suggested change
🧰 Tools🪛 markdownlint-cli2 (0.18.1)72-72: Heading levels should only increment by one level at a time (MD001, heading-increment) 🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| <StateExamplesGood1Snippet /> | ||||||||||||||||
|
|
||||||||||||||||
| ##### Single Class | ||||||||||||||||
|
|
||||||||||||||||
| <SingleStateExamplesGood1Snippet /> | ||||||||||||||||
|
|
||||||||||||||||
| ❌ **Bad** | ||||||||||||||||
|
|
||||||||||||||||
| <StateExamplesBad1Snippet /> | ||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| import 'package:flutter_bloc/flutter_bloc.dart'; | ||
| import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; | ||
| import 'package:easy_localization/easy_localization.dart'; | ||
| import 'package:web_dex/generated/codegen_loader.g.dart'; | ||
| import 'package:web_dex/bloc/message_signing/message_signing_event.dart'; | ||
| import 'package:web_dex/bloc/message_signing/message_signing_state.dart'; | ||
|
|
||
| class MessageSigningBloc | ||
| extends Bloc<MessageSigningEvent, MessageSigningState> { | ||
| final KomodoDefiSdk sdk; | ||
|
|
||
| MessageSigningBloc(this.sdk) : super(MessageSigningState.initial()) { | ||
| on<MessageSigningAddressesRequested>(_onLoadAddresses); | ||
| on<MessageSigningAddressSelected>(_onSelectAddress); | ||
| on<MessageSigningFormSubmitted>(_onSubmitMessage); | ||
| on<MessageSigningInputConfirmed>(_onRequestConfirmation); | ||
| on<MessageSigningConfirmationCancelled>(_onCancelConfirmation); | ||
| } | ||
|
|
||
| Future<void> _onLoadAddresses( | ||
| MessageSigningAddressesRequested event, | ||
| Emitter<MessageSigningState> emit, | ||
| ) async { | ||
| emit(state.copyWith( | ||
| status: MessageSigningStatus.loading, errorMessage: null)); | ||
|
|
||
| try { | ||
| final result = await sdk.pubkeys.getPubkeys(event.asset); | ||
| final keys = result.keys; | ||
|
|
||
| emit(state.copyWith( | ||
| addresses: keys, | ||
| selected: keys.isNotEmpty ? keys.first : null, | ||
| status: MessageSigningStatus.ready, | ||
| )); | ||
| } catch (e) { | ||
| emit(state.copyWith( | ||
| status: MessageSigningStatus.failure, | ||
| errorMessage: e.toString(), | ||
| )); | ||
| } | ||
| } | ||
|
|
||
| void _onSelectAddress( | ||
| MessageSigningAddressSelected event, | ||
| Emitter<MessageSigningState> emit, | ||
| ) { | ||
| emit(state.copyWith(selected: event.address)); | ||
| } | ||
|
|
||
| void _onRequestConfirmation( | ||
| MessageSigningInputConfirmed event, | ||
| Emitter<MessageSigningState> emit, | ||
| ) { | ||
| emit(state.copyWith(status: MessageSigningStatus.confirming)); | ||
| } | ||
|
|
||
| void _onCancelConfirmation( | ||
| MessageSigningConfirmationCancelled event, | ||
| Emitter<MessageSigningState> emit, | ||
| ) { | ||
| emit(state.copyWith(status: MessageSigningStatus.ready)); | ||
| } | ||
|
|
||
| Future<void> _onSubmitMessage( | ||
| MessageSigningFormSubmitted event, | ||
| Emitter<MessageSigningState> emit, | ||
| ) async { | ||
| final address = state.selected; | ||
| if (address == null) { | ||
| emit(state.copyWith( | ||
| errorMessage: LocaleKeys.pleaseSelectAddress.tr(), | ||
| status: MessageSigningStatus.failure, | ||
| )); | ||
| return; | ||
| } | ||
|
|
||
| emit(state.copyWith( | ||
| status: MessageSigningStatus.submitting, | ||
| errorMessage: null, | ||
| )); | ||
|
|
||
| try { | ||
| final signed = await sdk.messageSigning.signMessage( | ||
| coin: event.coinAbbr, | ||
| address: address.address, | ||
| message: event.message, | ||
| ); | ||
|
|
||
| emit(state.copyWith( | ||
| signedMessage: signed, | ||
| status: MessageSigningStatus.success, | ||
| )); | ||
| } catch (e) { | ||
| emit(state.copyWith( | ||
| errorMessage: LocaleKeys.failedToSignMessage.tr(args: [e.toString()]), | ||
| status: MessageSigningStatus.failure, | ||
| )); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import 'package:komodo_defi_types/komodo_defi_types.dart'; | ||
|
|
||
| sealed class MessageSigningEvent {} | ||
|
|
||
| class MessageSigningAddressesRequested extends MessageSigningEvent { | ||
| final Asset asset; | ||
|
|
||
| MessageSigningAddressesRequested(this.asset); | ||
| } | ||
|
|
||
| class MessageSigningAddressSelected extends MessageSigningEvent { | ||
| final PubkeyInfo address; | ||
|
|
||
| MessageSigningAddressSelected(this.address); | ||
| } | ||
|
|
||
| class MessageSigningFormSubmitted extends MessageSigningEvent { | ||
| final String message; | ||
| final String coinAbbr; | ||
|
|
||
| MessageSigningFormSubmitted({ | ||
| required this.message, | ||
| required this.coinAbbr, | ||
| }); | ||
| } | ||
|
|
||
| class MessageSigningInputConfirmed extends MessageSigningEvent {} | ||
| class MessageSigningConfirmationCancelled extends MessageSigningEvent {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import 'package:equatable/equatable.dart'; | ||
| import 'package:komodo_defi_types/komodo_defi_types.dart'; | ||
|
|
||
| enum MessageSigningStatus { | ||
| initial, | ||
| loading, | ||
| ready, | ||
| confirming, | ||
| submitting, | ||
| success, | ||
| failure, | ||
| } | ||
|
|
||
| class MessageSigningState extends Equatable { | ||
| final List<PubkeyInfo> addresses; | ||
| final PubkeyInfo? selected; | ||
| final String? signedMessage; | ||
| final String? errorMessage; | ||
| final MessageSigningStatus status; | ||
|
|
||
| const MessageSigningState({ | ||
| required this.addresses, | ||
| required this.selected, | ||
| required this.signedMessage, | ||
| required this.errorMessage, | ||
| required this.status, | ||
| }); | ||
|
|
||
| factory MessageSigningState.initial() => const MessageSigningState( | ||
| addresses: [], | ||
| selected: null, | ||
| signedMessage: null, | ||
| errorMessage: null, | ||
| status: MessageSigningStatus.initial, | ||
| ); | ||
|
|
||
| MessageSigningState copyWith({ | ||
| List<PubkeyInfo>? addresses, | ||
| PubkeyInfo? selected, | ||
| String? signedMessage, | ||
| String? errorMessage, | ||
| MessageSigningStatus? status, | ||
| }) { | ||
| return MessageSigningState( | ||
| addresses: addresses ?? this.addresses, | ||
| selected: selected ?? this.selected, | ||
| signedMessage: signedMessage ?? this.signedMessage, | ||
| errorMessage: errorMessage ?? this.errorMessage, | ||
| status: status ?? this.status, | ||
| ); | ||
| } | ||
|
Comment on lines
+37
to
+51
Contributor
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. 🧩 Analysis chainVerify if nullable fields need explicit null-setting. The If your Bloc logic requires explicitly nulling these fields during state transitions, consider using an Run the following script to check if any Bloc events or handlers attempt to explicitly null these fields: 🏁 Script executed: #!/bin/bash
# Description: Search for copyWith calls in MessageSigningBloc that may attempt to explicitly set nullable fields to null.
# Search for copyWith calls in the message_signing bloc and related files
rg -n -A 2 -B 2 --type=dart 'copyWith\s*\(' lib/bloc/message_signing/Length of output: 4449 copyWith doesn’t support explicit null resets In lib/bloc/message_signing/message_signing_state.dart (copyWith at lines 37–51), all nullable parameters fall back via
will not clear those fields but instead retain their previous values. Refactor |
||
|
|
||
| @override | ||
| List<Object?> get props => [ | ||
| addresses, | ||
| selected, | ||
| signedMessage, | ||
| errorMessage, | ||
| status, | ||
| ]; | ||
| } | ||
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.
Verify translation text on line 784.
The key
allowCustomFeehas the value"Allow custom seed", which appears inconsistent. Based on the key name, this should likely read"Allow custom fee"instead of"Allow custom seed".Apply this diff if the intention is to allow custom fees:
If the text is correct and refers to custom seeds, consider renaming the key to
allowCustomSeedfor clarity.📝 Committable suggestion
🤖 Prompt for AI Agents