Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion cold-wallet-app/lib/screens/scan_transaction_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ class ScanTransactionScreen extends StatefulWidget {
}

class _ScanTransactionScreenState extends State<ScanTransactionScreen> {
final MobileScannerController _controller = MobileScannerController();
// Unrestricted: deliver every decoded camera frame; dedup happens via _parts.
// QR-only: skipping the other symbologies shortens per-frame detection.
final MobileScannerController _controller = MobileScannerController(
detectionSpeed: DetectionSpeed.unrestricted,
formats: const [BarcodeFormat.qrCode],
);
final Set<String> _parts = {};
final Set<int> _seenSeq = {};
final RegExp _seqPattern = RegExp(r'/(\d+)-(\d+)/');
Expand Down
16 changes: 14 additions & 2 deletions cold-wallet-app/lib/screens/sign_transaction_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ class SignTransactionScreen extends ConsumerStatefulWidget {
}

class _SignTransactionScreenState extends ConsumerState<SignTransactionScreen> {
/// Tuning knobs for the animated signature QR (7 KB payload): bytes per frame
/// and how long each frame is shown. Larger fragments mean fewer, denser QRs.
static const _kUrFragmentLength = 1500;
static const _kUrFrameInterval = Duration(milliseconds: 100);

final ScrollController _scrollController = ScrollController();

ParsedPayload? _parsed;
Expand Down Expand Up @@ -101,7 +106,7 @@ class _SignTransactionScreenState extends ConsumerState<SignTransactionScreen> {
keypair: keypair,
message: QuantusSigningPayload.signablePayload(widget.payload),
);
final parts = encodeUr(data: signed);
final parts = encodeUr(data: signed, maxFragmentLength: _kUrFragmentLength);
setState(() {
_signing = false;
_signatureUr = parts;
Expand Down Expand Up @@ -400,7 +405,14 @@ class _SignTransactionScreenState extends ConsumerState<SignTransactionScreen> {
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
Center(child: AnimatedUrQr(parts: parts)),
Center(
child: AnimatedUrQr(
parts: parts,
interval: _kUrFrameInterval,
// Dense frames need as many screen pixels per QR module as possible.
size: (MediaQuery.sizeOf(context).width - 48).clamp(280.0, 500.0),
),
),
const SizedBox(height: 16),
if (parts.length > 1)
Text(
Expand Down
5 changes: 5 additions & 0 deletions mobile-app/assets/v2/keystone_dots_three.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions mobile-app/assets/v2/keystone_link.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion mobile-app/lib/v2/components/animated_qr_scanner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ class AnimatedQrScanner extends StatefulWidget {
}

class _AnimatedQrScannerState extends State<AnimatedQrScanner> {
final MobileScannerController _controller = MobileScannerController();
// Unrestricted: deliver every decoded camera frame; dedup happens via _parts.
// QR-only: skipping the other symbologies shortens per-frame detection.
final MobileScannerController _controller = MobileScannerController(
detectionSpeed: DetectionSpeed.unrestricted,
formats: const [BarcodeFormat.qrCode],
);
final Set<String> _parts = {};
final Set<int> _seenSequenceIndexes = {};

Expand Down
2 changes: 1 addition & 1 deletion mobile-app/lib/v2/components/qr_scanner_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class QrScannerPage extends ConsumerStatefulWidget {
}

class _QrScannerPageState extends ConsumerState<QrScannerPage> {
final _controller = MobileScannerController();
final _controller = MobileScannerController(formats: const [BarcodeFormat.qrCode]);
bool _scanned = false;

@override
Expand Down
30 changes: 30 additions & 0 deletions mobile-app/lib/v2/components/underlined_text_link.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart';

/// Centered tappable underlined text, used for secondary actions under a
/// primary button (e.g. "Cancel Transaction").
class UnderlinedTextLink extends StatelessWidget {
final String label;
final Color color;
final VoidCallback onTap;

const UnderlinedTextLink({super.key, required this.label, required this.color, required this.onTap});

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Center(
child: Text(
label,
style: context.themeText.smallParagraph?.copyWith(
color: color,
decoration: TextDecoration.underline,
decorationColor: color,
),
),
),
);
}
}
8 changes: 7 additions & 1 deletion mobile-app/lib/v2/screens/send/keystone_sign_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ class KeystoneSignScreen extends ConsumerStatefulWidget {
}

class _KeystoneSignScreenState extends ConsumerState<KeystoneSignScreen> {
/// Bytes per QR frame for the animated payload. The Keystone device scans
/// this QR, so frames must stay easy to read. SCALE-encoded transactions are
/// ~300 bytes, so 400-byte fragments put a typical transaction in a single
/// static QR (~version 21, well within Keystone's range).
static const _kUrFragmentLength = 400;

UnsignedTransactionData? _unsignedData;
List<String>? _urParts;
String? _error;
Expand Down Expand Up @@ -65,7 +71,7 @@ class _KeystoneSignScreenState extends ConsumerState<KeystoneSignScreen> {
widget.session.account,
widget.session.buildCall(),
);
final parts = encodeUr(data: unsigned.encodedPayloadRaw);
final parts = encodeUr(data: unsigned.encodedPayloadRaw, maxFragmentLength: _kUrFragmentLength);
if (parts.isEmpty) throw Exception('Failed to encode transaction payload as UR');
if (cacheKey != null) {
ref.read(keystoneSignCacheProvider.notifier).store(key: cacheKey, unsignedData: unsigned, urParts: parts);
Expand Down
3 changes: 2 additions & 1 deletion quantus_sdk/lib/src/rust/api/ur.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';

Uint8List decodeUr({required List<String> urParts}) => RustLib.instance.api.crateApiUrDecodeUr(urParts: urParts);

List<String> encodeUr({required List<int> data}) => RustLib.instance.api.crateApiUrEncodeUr(data: data);
List<String> encodeUr({required List<int> data, int? maxFragmentLength}) =>
RustLib.instance.api.crateApiUrEncodeUr(data: data, maxFragmentLength: maxFragmentLength);

bool isCompleteUr({required List<String> urParts}) => RustLib.instance.api.crateApiUrIsCompleteUr(urParts: urParts);
55 changes: 51 additions & 4 deletions quantus_sdk/lib/src/rust/frb_generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ abstract class RustLibApi extends BaseApi {

WormholeResult crateApiCryptoDeriveWormhole({required String mnemonicStr, required String path});

List<String> crateApiUrEncodeUr({required List<int> data});
List<String> crateApiUrEncodeUr({required List<int> data, int? maxFragmentLength});

Future<String> crateApiWormholeEnsureCircuitBinaries({required String binsDir});

Expand Down Expand Up @@ -451,23 +451,25 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
const TaskConstMeta(debugName: 'derive_wormhole', argNames: ['mnemonicStr', 'path']);

@override
List<String> crateApiUrEncodeUr({required List<int> data}) {
List<String> crateApiUrEncodeUr({required List<int> data, int? maxFragmentLength}) {
return handler.executeSync(
SyncTask(
callFfi: () {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_list_prim_u_8_loose(data, serializer);
sse_encode_opt_box_autoadd_u_32(maxFragmentLength, serializer);
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!;
},
codec: SseCodec(decodeSuccessData: sse_decode_list_String, decodeErrorData: sse_decode_String),
constMeta: kCrateApiUrEncodeUrConstMeta,
argValues: [data],
argValues: [data, maxFragmentLength],
apiImpl: this,
),
);
}

TaskConstMeta get kCrateApiUrEncodeUrConstMeta => const TaskConstMeta(debugName: 'encode_ur', argNames: ['data']);
TaskConstMeta get kCrateApiUrEncodeUrConstMeta =>
const TaskConstMeta(debugName: 'encode_ur', argNames: ['data', 'maxFragmentLength']);

@override
Future<String> crateApiWormholeEnsureCircuitBinaries({required String binsDir}) {
Expand Down Expand Up @@ -924,6 +926,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return dco_decode_proof_input(raw);
}

@protected
int dco_decode_box_autoadd_u_32(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as int;
}

@protected
Keypair dco_decode_keypair(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
Expand Down Expand Up @@ -970,6 +978,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
);
}

@protected
int? dco_decode_opt_box_autoadd_u_32(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw == null ? null : dco_decode_box_autoadd_u_32(raw);
}

@protected
U8Array32? dco_decode_opt_u_8_array_32(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
Expand Down Expand Up @@ -1110,6 +1124,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return (sse_decode_proof_input(deserializer));
}

@protected
int sse_decode_box_autoadd_u_32(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return (sse_decode_u_32(deserializer));
}

@protected
Keypair sse_decode_keypair(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
Expand Down Expand Up @@ -1164,6 +1184,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return MerkleProcessed(sortedSiblingsFlat: var_sortedSiblingsFlat, positions: var_positions);
}

@protected
int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs

if (sse_decode_bool(deserializer)) {
return (sse_decode_box_autoadd_u_32(deserializer));
} else {
return null;
}
}

@protected
U8Array32? sse_decode_opt_u_8_array_32(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
Expand Down Expand Up @@ -1327,6 +1358,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_proof_input(self, serializer);
}

@protected
void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_u_32(self, serializer);
}

@protected
void sse_encode_keypair(Keypair self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
Expand Down Expand Up @@ -1373,6 +1410,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_list_prim_u_8_strict(self.positions, serializer);
}

@protected
void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs

sse_encode_bool(self != null, serializer);
if (self != null) {
sse_encode_box_autoadd_u_32(self, serializer);
}
}

@protected
void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
Expand Down
18 changes: 18 additions & 0 deletions quantus_sdk/lib/src/rust/frb_generated.io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
ProofInput dco_decode_box_autoadd_proof_input(dynamic raw);

@protected
int dco_decode_box_autoadd_u_32(dynamic raw);

@protected
Keypair dco_decode_keypair(dynamic raw);

Expand All @@ -62,6 +65,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
MerkleProcessed dco_decode_merkle_processed(dynamic raw);

@protected
int? dco_decode_opt_box_autoadd_u_32(dynamic raw);

@protected
U8Array32? dco_decode_opt_u_8_array_32(dynamic raw);

Expand Down Expand Up @@ -117,6 +123,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
ProofInput sse_decode_box_autoadd_proof_input(SseDeserializer deserializer);

@protected
int sse_decode_box_autoadd_u_32(SseDeserializer deserializer);

@protected
Keypair sse_decode_keypair(SseDeserializer deserializer);

Expand All @@ -135,6 +144,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
MerkleProcessed sse_decode_merkle_processed(SseDeserializer deserializer);

@protected
int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer);

@protected
U8Array32? sse_decode_opt_u_8_array_32(SseDeserializer deserializer);

Expand Down Expand Up @@ -195,6 +207,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer);

@protected
void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer);

@protected
void sse_encode_keypair(Keypair self, SseSerializer serializer);

Expand All @@ -213,6 +228,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer);

@protected
void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer);

@protected
void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer);

Expand Down
18 changes: 18 additions & 0 deletions quantus_sdk/lib/src/rust/frb_generated.web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
ProofInput dco_decode_box_autoadd_proof_input(dynamic raw);

@protected
int dco_decode_box_autoadd_u_32(dynamic raw);

@protected
Keypair dco_decode_keypair(dynamic raw);

Expand All @@ -64,6 +67,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
MerkleProcessed dco_decode_merkle_processed(dynamic raw);

@protected
int? dco_decode_opt_box_autoadd_u_32(dynamic raw);

@protected
U8Array32? dco_decode_opt_u_8_array_32(dynamic raw);

Expand Down Expand Up @@ -119,6 +125,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
ProofInput sse_decode_box_autoadd_proof_input(SseDeserializer deserializer);

@protected
int sse_decode_box_autoadd_u_32(SseDeserializer deserializer);

@protected
Keypair sse_decode_keypair(SseDeserializer deserializer);

Expand All @@ -137,6 +146,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
MerkleProcessed sse_decode_merkle_processed(SseDeserializer deserializer);

@protected
int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer);

@protected
U8Array32? sse_decode_opt_u_8_array_32(SseDeserializer deserializer);

Expand Down Expand Up @@ -197,6 +209,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer);

@protected
void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer);

@protected
void sse_encode_keypair(Keypair self, SseSerializer serializer);

Expand All @@ -215,6 +230,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer);

@protected
void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer);

@protected
void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer);

Expand Down
Loading
Loading