Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f8e32c2
feat(auditor): add batch ID precision sweep (Number vs BigInt)
shogun444 Apr 26, 2026
5537fa7
feat(auditor): add single address precision comparison CLI
shogun444 Apr 26, 2026
7ddb758
feat: Add comprehensive table-driven tests for FilterDeposit
Apr 26, 2026
286298f
Merge pull request #2 from boalambo/feature/filter-deposit-table-driv…
boalambo Apr 26, 2026
67970e4
feat(auditor): implement robust batch sweep with error handling
shogun444 Apr 26, 2026
916e202
chore: remove unintended npm lockfile
shogun444 Apr 26, 2026
2da5eb7
feat(auditor): fix box alignment and improve fallback logging
shogun444 Apr 26, 2026
27f6b8f
feat: implement routing analysis feature
codebestia Apr 26, 2026
2bb0cfe
Merge pull request #1 from shogun444/feat/SingleAddress-Corruption-Co…
shogun444 Apr 27, 2026
d3ff407
Implement Decision Types and FilterDeposit Function (#220)
Apr 27, 2026
6386f26
Merge dev branch and resolve conflicts
Apr 27, 2026
2fc4010
Merge pull request #228 from shogun444/feat/BatchID-Corruption-Sweep
codeZe-us Apr 27, 2026
950cbc6
Merge remote-tracking branch 'shogun/dev' into feat/SingleAddress-Cor…
shogun444 Apr 27, 2026
1d482d7
fix: resolve conflict with origin/dev in single-address auditor
shogun444 Apr 27, 2026
f5649df
Merge pull request #230 from shogun444/feat/SingleAddress-Corruption-…
codeZe-us Apr 27, 2026
54fbfa6
Merge pull request #235 from codebestia/feat/drips-pr
codeZe-us Apr 27, 2026
b10f8bd
Consolidate filter implementations and tests (#220, #221)
Apr 28, 2026
fe16aa0
Merge pull request #233 from boalambo/dev
codeZe-us Apr 28, 2026
4db85a3
Fix: Resolve BLoC event name collision and import paths for analysis …
mansory-01 Apr 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions examples/flutter-demo/lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import 'core/theme/app_theme.dart';
import 'features/receive/domain/usecases/generate_deposit_instruction.dart';
import 'features/receive/presentation/bloc/receive_bloc.dart';
import 'features/analyze/domain/usecases/analyze_address.dart';
import 'features/analyze/presentation/bloc/analyze_bloc.dart';
import 'features/home/presentation/home_screen.dart';
import 'package:stellar_address_kit_demo/features/analyze/presentation/bloc/analyze_bloc.dart';
import 'package:stellar_address_kit_demo/features/safe_bloc.dart';
import 'package:stellar_address_kit_demo/features/unsafe_bloc.dart';
import 'package:stellar_address_kit_demo/features/home/presentation/home_screen.dart';

class App extends StatelessWidget {
const App({super.key});
Expand All @@ -24,6 +26,12 @@ class App extends StatelessWidget {
analyzeUseCase: AnalyzeAddress(),
),
),
BlocProvider(
create: (context) => SafeBloc(),
),
BlocProvider(
create: (context) => UnsafeBloc(),
),
],
child: MaterialApp(
title: 'Stellar Address Kit Demo',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import '../../receive/presentation/widgets/receive_panel.dart';
import '../../analyze/presentation/widgets/analyze_panel.dart';
import 'package:stellar_address_kit_demo/features/safe_panel.dart';
import 'package:stellar_address_kit_demo/features/unsafe_panel.dart';

class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
Expand All @@ -17,37 +19,45 @@ class HomeScreen extends StatelessWidget {
if (constraints.maxWidth > 900) {
return const Row(
children: [
Expanded(child: ReceivePanel()),
Expanded(child: UnsafePanel()),
VerticalDivider(width: 1),
Expanded(child: AnalyzePanel()),
VerticalDivider(width: 1),
Expanded(child: SafePanel()),
],
);
} else if (constraints.maxWidth > 600) {
return const SingleChildScrollView(
child: Column(
children: [
ReceivePanel(),
UnsafePanel(),
Divider(height: 1),
AnalyzePanel(),
Divider(height: 1),
SafePanel(),
],
),
);
} else {
return const DefaultTabController(
length: 2,
length: 4,
child: Column(
children: [
TabBar(
tabs: [
Tab(text: 'Receive', icon: Icon(Icons.download)),
Tab(text: 'Unsafe', icon: Icon(Icons.warning)),
Tab(text: 'Analyze', icon: Icon(Icons.search)),
Tab(text: 'Safe', icon: Icon(Icons.security)),
Tab(text: 'Receive', icon: Icon(Icons.download)),
],
),
Expanded(
child: TabBarView(
children: [
ReceivePanel(),
UnsafePanel(),
AnalyzePanel(),
SafePanel(),
ReceivePanel(),
],
),
),
Expand Down
62 changes: 62 additions & 0 deletions examples/flutter-demo/lib/features/safe_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:stellar_address_kit/stellar_address_kit.dart';

abstract class SafeEvent extends Equatable {
const SafeEvent();
@override
List<Object?> get props => [];
}

class SafeAddressChanged extends SafeEvent {
final String address;
const SafeAddressChanged(this.address);
@override
List<Object?> get props => [address];
}

abstract class SafeState extends Equatable {
const SafeState();
@override
List<Object?> get props => [];
}

class SafeInitial extends SafeState {}

class SafeDecoded extends SafeState {
final BigInt id;
const SafeDecoded(this.id);
@override
List<Object?> get props => [id];
}

class SafeError extends SafeState {
final String error;
const SafeError(this.error);
@override
List<Object?> get props => [error];
}

class SafeBloc extends Bloc<SafeEvent, SafeState> {
SafeBloc() : super(SafeInitial()) {
on<SafeAddressChanged>(_onAddressChanged);
}

void _onAddressChanged(SafeAddressChanged event, Emitter<SafeState> emit) {
if (event.address.isEmpty) {
emit(SafeInitial());
return;
}

try {
final parsed = StellarAddress.parse(event.address);
if (parsed.muxedId != null) {
emit(SafeDecoded(parsed.muxedId!));
} else {
emit(const SafeError('Not a muxed address'));
}
} catch (e) {
emit(SafeError(e.toString()));
}
}
}
151 changes: 151 additions & 0 deletions examples/flutter-demo/lib/features/safe_panel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:stellar_address_kit_demo/features/safe_bloc.dart';
import 'package:stellar_address_kit_demo/features/analyze/presentation/bloc/analyze_bloc.dart';

class SafePanel extends StatelessWidget {
const SafePanel({super.key});

@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.security, color: Colors.green),
const SizedBox(width: 8),
Text(
'Safe Decode (BigInt)',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
color: Colors.green[800],
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 8),
const Text(
'This panel uses the native BigInt path to ensure 100% precision on all platforms.',
style: TextStyle(color: Colors.grey),
),
const SizedBox(height: 24),
BlocBuilder<SafeBloc, SafeState>(
builder: (context, safeState) {
if (safeState is SafeDecoded) {
return BlocBuilder<AnalyzeBloc, AnalyzeState>(
builder: (context, analyzeState) {
BigInt? otherId;
if (analyzeState is AnalyzeSuccess) {
otherId = analyzeState.analysis.routingId;
}

final isCorrupted = otherId != null && otherId != safeState.id;

return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildIdCard(safeState.id, isCorrupted, otherId),
const SizedBox(height: 24),
if (isCorrupted) _buildCorruptionWarning(safeState.id, otherId!),
],
);
},
);
} else if (safeState is SafeError) {
return Center(child: Text(safeState.error, style: const TextStyle(color: Colors.red)));
}
return const Center(child: Text('Waiting for input...'));
},
),
],
),
);
}

Widget _buildIdCard(BigInt id, bool isCorrupted, BigInt? otherId) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isCorrupted ? Colors.orange : Colors.green.withOpacity(0.3),
width: 2,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'EXTRACTED ID',
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, color: Colors.grey),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'CORRECT',
style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold),
),
),
],
),
const SizedBox(height: 12),
SelectableText(
id.toString(),
style: const TextStyle(
fontSize: 24,
fontFamily: 'JetBrains Mono',
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
],
),
);
}

Widget _buildCorruptionWarning(BigInt correctId, BigInt corruptedId) {
final diff = (correctId - corruptedId).abs();
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.orange.withOpacity(0.3)),
),
child: Row(
children: [
const Icon(Icons.warning_amber_rounded, color: Colors.orange),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'PRECISION LOSS DETECTED',
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.orange, fontSize: 12),
),
const SizedBox(height: 4),
Text(
'The left panel shows a corrupted value. Difference: $diff',
style: const TextStyle(fontSize: 12),
),
],
),
),
],
),
);
}
}
67 changes: 67 additions & 0 deletions examples/flutter-demo/lib/features/unsafe_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:stellar_address_kit/stellar_address_kit.dart';

abstract class UnsafeEvent extends Equatable {
const UnsafeEvent();
@override
List<Object?> get props => [];
}

class UnsafeAddressChanged extends UnsafeEvent {
final String address;
const UnsafeAddressChanged(this.address);
@override
List<Object?> get props => [address];
}

abstract class UnsafeState extends Equatable {
const UnsafeState();
@override
List<Object?> get props => [];
}

class UnsafeInitial extends UnsafeState {}

class UnsafeDecoded extends UnsafeState {
final int id;
final bool corrupted;
const UnsafeDecoded(this.id, this.corrupted);
@override
List<Object?> get props => [id, corrupted];
}

class UnsafeError extends UnsafeState {
final String error;
const UnsafeError(this.error);
@override
List<Object?> get props => [error];
}

class UnsafeBloc extends Bloc<UnsafeEvent, UnsafeState> {
UnsafeBloc() : super(UnsafeInitial()) {
on<UnsafeAddressChanged>(_onAddressChanged);
}

void _onAddressChanged(UnsafeAddressChanged event, Emitter<UnsafeState> emit) {
if (event.address.isEmpty) {
emit(UnsafeInitial());
return;
}

try {
final parsed = StellarAddress.parse(event.address);
if (parsed.muxedId != null) {
final realId = parsed.muxedId!;
final idString = realId.toString();
final unsafeId = int.parse(idString);
final isCorrupted = BigInt.from(unsafeId) != realId;
emit(UnsafeDecoded(unsafeId, isCorrupted));
} else {
emit(const UnsafeError('Not a muxed address'));
}
} catch (e) {
emit(UnsafeError(e.toString()));
}
}
}
Loading
Loading