diff --git a/assets/translations/en.json b/assets/translations/en.json
index 80c2e8a79e..ca44d18b41 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -764,5 +764,36 @@
"fetchingPrivateKeysTitle": "Fetching Private Keys...",
"fetchingPrivateKeysMessage": "Please wait while we securely fetch your private keys...",
"pubkeyType": "Type",
- "securitySettings": "Security Settings"
-}
\ No newline at end of file
+ "securitySettings": "Security Settings",
+ "sendToAddress": "Only send {} to this address",
+ "message": "Message",
+ "signMessage": "Sign Message",
+ "selectedAddress": "Selected Address",
+ "selectAddress": "Select Address",
+ "messageToSign": "Message to Sign",
+ "enterMessage": "Enter message",
+ "signMessageButton": "Sign Message",
+ "signedMessage": "Signed Message",
+ "pleaseSelectAddress": "Please select an address first",
+ "pleaseEnterMessage": "Please enter a message to sign",
+ "failedToSignMessage": "Failed to sign message: {}",
+ "swapCoin": "Swap",
+ "komodoWalletSeed": "Komodo Wallet seed",
+ "failedToLoadAddresses": "Failed to load addresses: {}",
+ "sendFeedbackButton": "Share your feedback",
+ "allowCustomFee": "Allow custom seed",
+ "copyToClipboard": "Copy to clipboard",
+ "copyAllDetails": "Copy all details",
+ "zeroBalanceTooltip": "Insufficient balance to use Bitrefill",
+ "swapAddress": "Swap Address",
+ "userNotFoundError": "User not found",
+ "loginFailedError": "Login failed",
+ "previewWithdrawal": "Preview Withdrawal",
+ "chart": "Chart",
+ "confirmMessageSigning": "Confirm Message Signing",
+ "messageSigningWarning": "Only sign messages from trusted sources.",
+ "messageSigningCheckboxText": "I understand that signing proves ownership of this address.",
+ "messageSigned": "Message signed",
+ "addressLabel": "Address - ",
+ "signingAddress": "Signing address"
+}
diff --git a/docs/BLOC_NAMING_CONVENTIONS.md b/docs/BLOC_NAMING_CONVENTIONS.md
new file mode 100644
index 0000000000..e6443a9c42
--- /dev/null
+++ b/docs/BLOC_NAMING_CONVENTIONS.md
@@ -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**
+
+
+
+❌ **Bad**
+
+
+
+## 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
+
+
+
+##### Single Class
+
+
+
+❌ **Bad**
+
+
diff --git a/lib/bloc/message_signing/message_signing_bloc.dart b/lib/bloc/message_signing/message_signing_bloc.dart
new file mode 100644
index 0000000000..306dba711c
--- /dev/null
+++ b/lib/bloc/message_signing/message_signing_bloc.dart
@@ -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 {
+ final KomodoDefiSdk sdk;
+
+ MessageSigningBloc(this.sdk) : super(MessageSigningState.initial()) {
+ on(_onLoadAddresses);
+ on(_onSelectAddress);
+ on(_onSubmitMessage);
+ on(_onRequestConfirmation);
+ on(_onCancelConfirmation);
+ }
+
+ Future _onLoadAddresses(
+ MessageSigningAddressesRequested event,
+ Emitter 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 emit,
+ ) {
+ emit(state.copyWith(selected: event.address));
+ }
+
+ void _onRequestConfirmation(
+ MessageSigningInputConfirmed event,
+ Emitter emit,
+ ) {
+ emit(state.copyWith(status: MessageSigningStatus.confirming));
+ }
+
+ void _onCancelConfirmation(
+ MessageSigningConfirmationCancelled event,
+ Emitter emit,
+ ) {
+ emit(state.copyWith(status: MessageSigningStatus.ready));
+ }
+
+ Future _onSubmitMessage(
+ MessageSigningFormSubmitted event,
+ Emitter 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,
+ ));
+ }
+ }
+}
diff --git a/lib/bloc/message_signing/message_signing_event.dart b/lib/bloc/message_signing/message_signing_event.dart
new file mode 100644
index 0000000000..475f07cfb4
--- /dev/null
+++ b/lib/bloc/message_signing/message_signing_event.dart
@@ -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 {}
\ No newline at end of file
diff --git a/lib/bloc/message_signing/message_signing_state.dart b/lib/bloc/message_signing/message_signing_state.dart
new file mode 100644
index 0000000000..3ef8c68cca
--- /dev/null
+++ b/lib/bloc/message_signing/message_signing_state.dart
@@ -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 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? 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,
+ );
+ }
+
+ @override
+ List