Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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: 4 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[submodule "crypto_plugins/flutter_libepiccash"]
path = crypto_plugins/flutter_libepiccash
url = https://github.com/cypherstack/flutter_libepiccash.git
[submodule "crypto_plugins/frostdart"]
path = crypto_plugins/frostdart
url = https://github.com/cypherstack/frostdart
[submodule "crypto_plugins/flutter_libmwc"]
path = crypto_plugins/flutter_libmwc
url = https://github.com/cypherstack/flutter_libmwc
[submodule "crypto_plugins/flutter_libepiccash"]
path = crypto_plugins/flutter_libepiccash
url = https://github.com/who-biz/flutter_libepiccash.git
branch = ebox-cancel-tx
2 changes: 2 additions & 0 deletions lib/models/isar/models/blockchain_data/v2/transaction_v2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class TransactionV2 {
int? get numberOfMessages =>
_getFromOtherData(key: TxV2OdKeys.numberOfMessages) as int?;
String? get slateId => _getFromOtherData(key: TxV2OdKeys.slateId) as String?;
String? get epicboxId => _getFromOtherData(key: TxV2OdKeys.epicboxId) as String?;
String? get onChainNote =>
_getFromOtherData(key: TxV2OdKeys.onChainNote) as String?;
bool get isCancelled =>
Expand Down Expand Up @@ -409,6 +410,7 @@ abstract final class TxV2OdKeys {
static const isMimblewimblecoinTransaction = "isMimblewimblecoinTransaction";
static const numberOfMessages = "numberOfMessages";
static const slateId = "slateId";
static const epicboxId = "epicboxId";
static const onChainNote = "onChainNote";
static const isCancelled = "isCancelled";
static const contractAddress = "contractAddress";
Expand Down
67 changes: 37 additions & 30 deletions lib/utilities/address_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,45 +44,46 @@ class AddressUtils {
static Map<String, String> _parseUri(String uri) {
final Map<String, String> result = {};
try {
final u = Uri.parse(uri);
if (u.hasScheme) {
result["scheme"] = u.scheme.toLowerCase();
final Uri parsedUri = Uri.parse(uri);

if (parsedUri.hasScheme) {
final String scheme = parsedUri.scheme.toLowerCase();
result["scheme"] = scheme;

// Handle different URI formats.
if (result["scheme"] == "bitcoin" ||
result["scheme"] == "bitcoincash") {
result["address"] = u.path;
result["address"] = parsedUri.path;
} else if (result["scheme"] == "monero") {
// Monero addresses can contain '?' which Uri.parse interprets as query start.
final addressEnd = uri.indexOf(
'?',
7,
); // 7 is the length of "monero:".
if (addressEnd != -1) {
result["address"] = uri.substring(7, addressEnd);
} else {
result["address"] = uri.substring(7);
}
final int addressEnd = uri.indexOf(
"?",
7, // 7 is the length of "monero:".
);
} else {
// Default case, treat path as address.
result["address"] = u.path;
result["address"] = parsedUri.path;
}
} else {
// Plain address, including an Epicbox/Grinbox address containing '@'.
result["address"] = parsedUri.path;
}

// Parse query parameters.
result.addAll(_parseQueryParameters(u.queryParameters));
// Parse query parameters.
result.addAll(_parseQueryParameters(parsedUri.queryParameters));

// Handle Monero-specific fragment (tx_description).
if (u.fragment.isNotEmpty && result["scheme"] == "monero") {
result["tx_description"] = Uri.decodeComponent(u.fragment);
}
// Handle Monero-specific fragment (tx_description).
if (parsedUri.fragment.isNotEmpty && result["scheme"] == "monero") {
result["tx_description"] = Uri.decodeComponent(parsedUri.fragment);
}
} catch (e, s) {
Logging.instance.d(
"Exception caught in parseUri($uri): $e",
"Exception caught in _parseUri($uri): $e",
error: e,
stackTrace: s,
);
}

return result;
}

Expand Down Expand Up @@ -137,6 +138,7 @@ class AddressUtils {
static PaymentUriData? parsePaymentUri(String uri, {Logging? logging}) {
// hacky check its not just a bcash, ecash, or xel address
final parts = uri.split(":");

if (parts.length == 2) {
if ([
"xel",
Expand All @@ -153,20 +155,25 @@ class AddressUtils {
final Map<String, String> parsedData = _parseUri(uri);

// Normalize the URI scheme.
final String scheme = parsedData['scheme'] ?? '';
parsedData.remove('scheme');
final String scheme = parsedData["scheme"] ?? "";
parsedData.remove("scheme");

// Filter out unrecognized parameters.
final filteredParams = _filterParams(parsedData);
final String? address = parsedData["address"];

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do proper null-checking up here now - See comment on L169, item 1.


if (address == null || address.trim().isEmpty) {
return null;
}

final Map<String, String> filteredParams = _filterParams(parsedData);

return PaymentUriData(
scheme: scheme,
address: parsedData['address']!.trim(),
amount: filteredParams['amount'] ?? filteredParams['tx_amount'],
label: filteredParams['label'] ?? filteredParams['recipient_name'],
message: filteredParams['message'] ?? filteredParams['tx_description'],
paymentId: filteredParams['tx_payment_id'],
// Specific to Monero

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.) check that the parsedData['address']!.trim() change to address.trim() doesn't cause issues for other coins.
2.) ensure comment // Specific to Monero is written back into PR code (we want diff to be absolutely minimal).

address: address.trim(),
amount: filteredParams["amount"] ?? filteredParams["tx_amount"],
label: filteredParams["label"] ?? filteredParams["recipient_name"],
message: filteredParams["message"] ?? filteredParams["tx_description"],
paymentId: filteredParams["tx_payment_id"],
additionalParams: filteredParams,
);
} catch (e, s) {
Expand Down
20 changes: 18 additions & 2 deletions lib/wallets/wallet/impl/epiccash_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,12 @@ class EpiccashWallet extends Bip39Wallet {
throw Exception('Wallet not initialized');
}

final result = await libEpic.cancelTransaction(
final epicboxConfig = await getEpicBoxConfig();
final result = await libEpic.cancelEpicboxTransaction(
wallet: _wallet!,
transactionId: txSlateId,
methodIsEpicbox: true,
epicboxConfig: epicboxConfig.toString(),
txSlateId: txSlateId,
);
Logging.instance.d("cancel $txSlateId result: $result");
return result;
Expand Down Expand Up @@ -1386,10 +1389,22 @@ class EpiccashWallet extends Bip39Wallet {
final slatesToCommits = info.epicData?.slatesToCommits ?? {};

for (final tx in transactions) {

Logging.instance.w(

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs removed before prod.

"EPIC TX "
"id=${tx.id} "
"slate=${tx.txSlateId} "
"epicbox_tx_id=${tx.txEpicboxId} "
"type=${tx.txType} "
"sentCancelled=${libEpic.txTypeIsSentCancelled(tx.txType)} "
"receiveCancelled=${libEpic.txTypeIsReceiveCancelled(tx.txType)}",
);

final isIncoming =
libEpic.txTypeIsReceived(tx.txType) ||
libEpic.txTypeIsReceiveCancelled(tx.txType);
final slateId = tx.txSlateId;
final epicboxId = tx.txEpicboxId;
final commitId = slatesToCommits[slateId]?['commitId'] as String?;
final numberOfMessages = tx.messages?.length;
final onChainNote = tx.messages?.first.message;
Expand Down Expand Up @@ -1451,6 +1466,7 @@ class EpiccashWallet extends Bip39Wallet {
"isEpiccashTransaction": true,
"numberOfMessages": numberOfMessages,
"slateId": slateId,
"epicboxId": epicboxId,
"onChainNote": onChainNote,
"isCancelled":
libEpic.txTypeIsSentCancelled(tx.txType) ||
Expand Down
11 changes: 9 additions & 2 deletions lib/wl_gen/interfaces/libepiccash_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,13 @@ abstract class LibEpicCashInterface {
required String slateJson,
});

Future<String> cancelTransaction({
Future<String> cancelEpicboxTransaction({
required DynamicObject wallet,
required String transactionId,
required bool methodIsEpicbox,
String? epicboxConfig,
int? txId,
String? txSlateId,
String? txEpicboxId,
});

Future<List<EpicTransaction>> getTransactions({
Expand Down Expand Up @@ -135,6 +139,7 @@ class EpicTransaction {
final String parentKeyId;
final int id;
final String? txSlateId;
final String? txEpicboxId;
final Enum txType;
final String creationTs;
final String confirmationTs;
Expand All @@ -155,6 +160,7 @@ class EpicTransaction {
required this.parentKeyId,
required this.id,
this.txSlateId,
this.txEpicboxId,
required this.txType,
required this.creationTs,
required this.confirmationTs,
Expand All @@ -177,6 +183,7 @@ class EpicTransaction {
return 'EpicTransaction('
'id: $id, '
'txSlateId: $txSlateId, '
'txEpicboxId: $txEpicboxId, '
'type: $txType, '
'confirmed: $confirmed, '
'inputs: $numInputs, '
Expand Down
35 changes: 30 additions & 5 deletions tool/wl_templates/EPIC_libepiccash_interface_impl.template.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,25 @@ final class _LibEpicCashInterfaceImpl extends LibEpicCashInterface {
const _LibEpicCashInterfaceImpl();

@override
Future<String> cancelTransaction({
Future<String> cancelEpicboxTransaction({
required DynamicObject wallet,
required String transactionId,
}) {
return wallet.get<EpicWallet>().cancelTransaction(
transactionId: transactionId,
required bool methodIsEpicbox,
String? epicboxConfig,
int? txId,
String? txSlateId,
String? txEpicboxId,
}) async {
final epicWallet = wallet.get<EpicWallet>();

if (epicboxConfig != null) {
epicWallet.updateEpicboxConfig(epicboxConfig);
}

return epicWallet.cancelEpicboxTransaction(
methodIsEpicbox: methodIsEpicbox,
txId: txId,
txSlateId: txSlateId,
txEpicboxId: txEpicboxId,
);
}

Expand Down Expand Up @@ -124,6 +137,17 @@ final class _LibEpicCashInterfaceImpl extends LibEpicCashInterface {
refreshFromNode: refreshFromNode,
);

// Log the flutter_libepiccash Transaction BEFORE converting it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be removed before prod.

for (final e in transactions) {
print(
"EPIC INTERFACE TX "
"id=${e.id} "
"txSlateId=${e.txSlateId} "
"txEpicboxId=${e.txEpicboxId} "
"type=${e.txType}",
);
}

return transactions
.map(
(e) => EpicTransaction(
Expand All @@ -138,6 +162,7 @@ final class _LibEpicCashInterfaceImpl extends LibEpicCashInterface {
amountCredited: e.amountCredited,
amountDebited: e.amountDebited,
txSlateId: e.txSlateId,
txEpicboxId: e.txEpicboxId,
fee: e.fee,
ttlCutoffHeight: e.ttlCutoffHeight,
messages: e.messages?.messages
Expand Down