Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1020f28
Initial
dev2-nomo Jan 9, 2025
2c9c78c
Initial
dev2-nomo Jan 9, 2025
bcc503a
Migrate to builtIn HDNode insteaf of bip32
dev2-nomo Jan 13, 2025
610a5e6
Fix Lint
dev2-nomo Jan 13, 2025
d65d3de
Fix Pipe
dev2-nomo Jan 13, 2025
e02dc68
Refactor derivation functions to use NetworkNodeInfo and simplify par…
dev2-nomo Jan 13, 2025
b2ad177
Add command framework with Help and Exit commands to CLI Example
dev2-nomo Jan 13, 2025
0659823
Bip39 impl (#141)
dev2-nomo Jan 30, 2025
13540e8
Update http package to version 1.3.0 and SDK constraint to 3.3.0
dev2-nomo Jan 30, 2025
ae830e8
Merge branch 'main' of https://github.com/nomo-app/walletkit-dart int…
dev2-nomo Jan 30, 2025
ae29d9f
Fix Lint
dev2-nomo Jan 30, 2025
dbda488
test: Enhance BIP39 tests to validate extended private key generation
dev2-nomo Jan 30, 2025
3731128
refactor: Comment out mnemonic generation test for word 'arm'
dev2-nomo Jan 30, 2025
353d1f0
feat: Add TODO for future test cases in multiple languages
dev2-nomo Jan 30, 2025
a48eac4
Update sdk
dev2-nomo Mar 20, 2025
806441e
Merge branch 'main' of https://github.com/nomo-app/walletkit-dart int…
dev2-nomo Mar 20, 2025
db65476
Segwit fee fix (#149)
dev2-nomo Mar 20, 2025
4997ead
Initial (#130)
nomo-app Mar 20, 2025
8137b73
format
dev2-nomo Mar 20, 2025
41abf45
Add cursorignore
dev2-nomo Mar 24, 2025
fea2619
Update Readme and add License
dev2-nomo Mar 24, 2025
d94e931
Start Refactor
dev2-nomo Mar 24, 2025
602f9d0
Fix Imports
dev2-nomo Mar 26, 2025
8b8c9b0
Fix Input Output Length Byte
ThomasFercher Sep 26, 2025
b700dbd
Wallet Impl First Try
ThomasFercher Sep 28, 2025
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
1 change: 0 additions & 1 deletion example/web3_kit_example.dart

This file was deleted.

13 changes: 13 additions & 0 deletions example/wkdart.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'dart:io';

Comment on lines +2 to +3
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add necessary wallet-related imports and error handling.

As this is a wallet interface implementation, the file should import the necessary wallet-related modules and implement proper error handling for I/O operations.

Consider adding:

 import 'dart:io';
+import 'package:walletkit_dart/walletkit_dart.dart';
+import 'dart:async';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import 'dart:io';
import 'dart:io';
import 'package:walletkit_dart/walletkit_dart.dart';
import 'dart:async';

void main(List<String> args) {
print("Welcome to Walletkit-Dart");

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance main function with wallet initialization and CLI argument handling.

The current implementation lacks essential wallet functionality initialization and doesn't utilize the command-line arguments.

Consider implementing:

  1. Command-line argument parsing for wallet operations
  2. Wallet initialization
  3. Usage instructions
  4. Error handling

Example structure:

void main(List<String> args) async {
  try {
    if (args.contains('--help')) {
      printUsage();
      return;
    }
    
    print("Welcome to Walletkit-Dart");
    final wallet = await WalletKit.initialize();
    
    // Interactive loop here
    
  } catch (e) {
    print("Error: $e");
    exit(1);
  }
}

while (true) {
final input = stdin.readLineSync();

if (input == 'exit') {
break;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement proper command processing and async I/O.

The current interactive loop implementation has several limitations:

  1. Uses blocking I/O
  2. Lacks command processing
  3. No error handling
  4. No user feedback

Consider implementing:

while (true) {
  stdout.write('wallet> ');
  String? input;
  try {
    input = await stdin.readLineSync();
    if (input == null) continue;
    
    if (input == 'exit') {
      break;
    }
    
    final result = await processCommand(input);
    print(result);
  } catch (e) {
    print('Error: $e');
  }
}

Future<String> processCommand(String input) async {
  final parts = input.split(' ');
  switch (parts[0]) {
    case 'balance':
      return await wallet.getBalance();
    case 'send':
      if (parts.length != 3) return 'Usage: send <address> <amount>';
      return await wallet.send(parts[1], double.parse(parts[2]));
    case 'help':
      return '''
Available commands:
  balance - Show wallet balance
  send <address> <amount> - Send funds
  exit - Exit the application
''';
    default:
      return 'Unknown command. Type "help" for available commands.';
  }
}

}
121 changes: 78 additions & 43 deletions lib/src/crypto/network_type.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ignore_for_file: constant_identifier_names, camel_case_types

import 'package:bip32/bip32.dart' as bip32;
import 'package:collection/collection.dart';
import 'package:walletkit_dart/walletkit_dart.dart';

sealed class NetworkType {
Expand Down Expand Up @@ -76,60 +76,95 @@ sealed class UTXONetworkType extends NetworkType {
}
}

typedef BIP32Prefixes = ({int private, int public});

class NetworkBIP {
final int wif;
final int bip32XpubPrefix;
final int bip32XprivPrefix;
// final int bip44XpubPrefix;
// final int bip44XprivPrefix;
final int bip49XpubPrefix;
final int bip49XprivPrefix;
final int bip84XpubPrefix;
final int bip84XprivPrefix;

final BIP32Prefixes bip32;
final BIP32Prefixes bip49;
final BIP32Prefixes bip84;
final BIP32Prefixes bip49MultiSig;
final BIP32Prefixes bip84MultiSig;

static const String NS_PURPOSE = "m/0'";
static const String BIP44_PURPOSE = "m/44'";
static const String BIP49_PURPOSE = "m/49'";
static const String BIP84_PURPOSE = "m/84'";

static List<NetworkBIP> defaults = [
BITCOIN_NETWORK_BIP,
LTC_NETWORK_BIP,
DOGE_NETWORK_BIP,
];

static (BIP32Prefixes, NetworkBIP)? findPrefixesFromVersion(int version) {
return defaults
.map((e) {
final prefixes = e.fromVersion(version);
if (prefixes != null) {
return (prefixes, e);
}
return null;
})
.nonNulls
.firstOrNull;
}

const NetworkBIP({
required this.bip32XpubPrefix,
required this.bip32XprivPrefix,
// required this.bip44XpubPrefix,
// required this.bip44XprivPrefix,
required this.bip49XpubPrefix,
required this.bip49XprivPrefix,
required this.bip84XpubPrefix,
required this.bip84XprivPrefix,
required this.bip32,
required this.bip49,
required this.bip84,
required this.bip49MultiSig,
required this.bip84MultiSig,
required this.wif,
});

bip32.NetworkType getForWalletType(HDWalletPurpose purpose) =>
switch (purpose) {
HDWalletPurpose.NO_STRUCTURE ||
HDWalletPurpose.BIP44 =>
bip32.NetworkType(
wif: wif,
bip32: bip32.Bip32Type(
private: bip32XprivPrefix,
public: bip32XpubPrefix,
),
),
HDWalletPurpose.BIP84 => bip32.NetworkType(
wif: wif,
bip32: bip32.Bip32Type(
private: bip84XprivPrefix,
public: bip84XpubPrefix,
),
),
HDWalletPurpose.BIP49 => bip32.NetworkType(
wif: wif,
bip32: bip32.Bip32Type(
private: bip49XprivPrefix,
public: bip49XpubPrefix,
),
),
};
BIP32Prefixes? fromVersion(int version) {
if (version == bip32.private || version == bip32.public) {
return bip32;
}
if (version == bip49.private || version == bip49.public) {
return bip49;
}
if (version == bip84.private || version == bip84.public) {
return bip84;
}
if (version == bip49MultiSig.private || version == bip49MultiSig.public) {
return bip49MultiSig;
}
if (version == bip84MultiSig.private || version == bip84MultiSig.public) {
return bip84MultiSig;
}
return null;
}

// bip32.NetworkType getForWalletType(HDWalletPurpose purpose) =>
// switch (purpose) {
// HDWalletPurpose.NO_STRUCTURE ||
// HDWalletPurpose.BIP44 =>
// bip32.NetworkType(
// wif: wif,
// bip32: bip32.Bip32Type(
// private: bip32PrivPrefix,
// public: bip32PubPrefix,
// ),
// ),
// HDWalletPurpose.BIP84 => bip32.NetworkType(
// wif: wif,
// bip32: bip32.Bip32Type(
// private: bip84PrivPrefix,
// public: bip84PubPrefix,
// ),
// ),
// HDWalletPurpose.BIP49 => bip32.NetworkType(
// wif: wif,
// bip32: bip32.Bip32Type(
// private: bip49PrivPrefix,
// public: bip49PubPrefix,
// ),
// ),
// };
}

class SighashInfo {
Expand Down
12 changes: 1 addition & 11 deletions lib/src/crypto/tron/entities/tron_address.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import 'dart:typed_data';
import 'package:walletkit_dart/src/crypto/utxo/utils/pubkey_to_address.dart';
import 'package:walletkit_dart/src/crypto/utxo/utils/ecurve.dart';
import 'package:walletkit_dart/src/utils/base58.dart';
import 'package:walletkit_dart/src/utils/crypto.dart';
import 'package:walletkit_dart/src/utils/keccak.dart';
import 'package:walletkit_dart/walletkit_dart.dart';

extension BIP32Extension on BipNode {
Uint8List get publicKeyUncompressed {
if (privateKey == null) {
throw UnsupportedError("privateKey is null");
}
return pointFromScalar(privateKey!, false)!;
}
}

String uncompressedPublicKeyToAddress(Uint8List publicKey, int prefix) {
final addressInput = publicKey.sublist(1);
final publicKeyHash = keccak256(addressInput);
Expand Down
1 change: 1 addition & 0 deletions lib/src/crypto/tron/entities/tron_transaction_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:fixnum/fixnum.dart';
import 'package:walletkit_dart/src/crypto/tron/repositories/rpc/core/Tron.pb.dart';

import 'package:walletkit_dart/src/crypto/utxo/utils/pubkey_to_address.dart';
import 'package:walletkit_dart/src/utils/crypto.dart';
import 'package:walletkit_dart/walletkit_dart.dart';
import 'package:walletkit_dart/src/crypto/tron/repositories/rpc/core/Tron.pb.dart'
as tron;
Expand Down
1 change: 1 addition & 0 deletions lib/src/crypto/utxo/entities/payments/p2h.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:walletkit_dart/src/crypto/utxo/entities/op_codes.dart';
import 'package:walletkit_dart/src/crypto/utxo/utils/pubkey_to_address.dart';
import 'package:walletkit_dart/src/domain/exceptions.dart';
import 'package:walletkit_dart/src/utils/base32.dart';
import 'package:walletkit_dart/src/utils/crypto.dart';
import 'package:walletkit_dart/walletkit_dart.dart';

class P2Hash {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:walletkit_dart/src/crypto/utxo/entities/payments/pk_script_conve
import 'package:walletkit_dart/src/crypto/utxo/utils/pubkey_to_address.dart';
import 'package:walletkit_dart/src/crypto/utxo/entities/raw_transaction/input.dart';
import 'package:walletkit_dart/src/crypto/utxo/entities/raw_transaction/output.dart';
import 'package:walletkit_dart/src/utils/crypto.dart';
import 'package:walletkit_dart/src/utils/int.dart';
import 'package:walletkit_dart/src/utils/var_uint.dart';
import 'package:walletkit_dart/walletkit_dart.dart';
Expand Down
82 changes: 17 additions & 65 deletions lib/src/crypto/utxo/utils/derivation.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import 'dart:typed_data';
import 'package:bip32/bip32.dart' as bip32;
import 'package:walletkit_dart/src/crypto/utxo/utils/pubkey_to_address.dart';
import 'package:bs58check/bs58check.dart' as bs58check;
import 'package:walletkit_dart/src/utils/var_uint.dart';
import 'package:walletkit_dart/src/wallet/hd_node.dart';
import 'package:walletkit_dart/walletkit_dart.dart';

typedef BipNode = bip32.BIP32;

String deriveExtendedPubKey({
required Uint8List seed,
required HDWalletPath walletPurpose,
UTXONetworkType? type,
required UTXONetworkType type,
}) {
///
/// Walletkit Compatibility
Expand All @@ -37,18 +34,15 @@ String deriveExtendedPubKey({
networkType: type,
walletPath: walletPurpose,
);
return masterNode.neutered().toBase58();
return masterNode.neutered().extendedPublicKey();
}

BipNode deriveMasterNodeFromSeed({
HDNode deriveMasterNodeFromSeed({
required Uint8List seed,
required HDWalletPath walletPath,
UTXONetworkType? networkType,
required UTXONetworkType networkType,
}) {
final bipNetworkType =
networkType?.networkBIP.getForWalletType(walletPath.purpose);

final parentNode = BipNode.fromSeed(seed, bipNetworkType);
final parentNode = HDNode.fromSeed(seed, network: networkType.networkBIP);
final derivationPath = switch (walletPath.basePath) {
"m/44'/2'" => walletPath.account0Path,
_ => walletPath.purpose.string,
Expand All @@ -59,60 +53,18 @@ BipNode deriveMasterNodeFromSeed({
return node;
}

BipNode deriveMasterNodeFromExtendedKeyWithCheck({
required String ePubKey,
required UTXONetworkType networkType,
required HDWalletPurpose purpose,
}) {
final (node, version) = deriveMasterNodeFromExtendedKey(
ePubKey,
networkType: networkType,
purpose: purpose,
);

if (version != node.network.bip32.private &&
version != node.network.bip32.public) {
throw ArgumentError(
"Version mismatch. Extracted Version: $version. Expected: ${node.network.bip32.private} or ${node.network.bip32.public}",
);
}

return node;
}

(BipNode node, int version) deriveMasterNodeFromExtendedKey(
HDNode deriveMasterNodeFromExtendedKey(
String ePubKey, {
UTXONetworkType? networkType,
HDWalletPurpose? purpose,
}) {
final buffer = bs58check.decode(ePubKey);

if (buffer.length != 78) {
throw UnsupportedError("invalid ePubKey");
}

final version = buffer.bytes.getUint32(0);

final node = BipNode.fromBase58(
return HDNode.fromExtendedKey(
ePubKey,
switch ((networkType, purpose)) {
(UTXONetworkType network, HDWalletPurpose purpose) =>
network.networkBIP.getForWalletType(purpose),
_ => bip32.NetworkType(
wif: 0x80,
bip32: bip32.Bip32Type(
private: 0x0488ADE4,
public: 0x0488B21E,
),
),
},
network: networkType?.networkBIP,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Handle potential null values for networkType

In deriveMasterNodeFromExtendedKey, the optional networkType parameter is used to access networkType?.networkBIP. If networkType is null, this could lead to unexpected behavior in HDNode.fromExtendedKey. Consider providing a default network type or adding checks to handle the null case appropriately.

Apply this diff to handle null networkType:

- return HDNode.fromExtendedKey(
-   ePubKey,
-   network: networkType?.networkBIP,
- );
+ final network = networkType?.networkBIP ?? defaultNetworkBIP;
+ return HDNode.fromExtendedKey(
+   ePubKey,
+   network: network,
+ );

Replace defaultNetworkBIP with the appropriate default value.

Committable suggestion skipped: line range outside the PR's diff.

);

return (node, version);
}

NodeWithAddress deriveChildNode({
required BipNode masterNode,
required HDNode masterNode,
required int chainIndex,
required int index,
required UTXONetworkType networkType,
Expand Down Expand Up @@ -150,11 +102,11 @@ NodeWithAddress deriveChildNode({
);
}

bip32.BIP32 deriveChildNodeFromPath({
HDNode deriveChildNodeFromPath({
required Uint8List seed,
required String childDerivationPath,
required HDWalletPath walletPath,
UTXONetworkType? networkType,
required UTXONetworkType networkType,
}) {
final masterNode = deriveMasterNodeFromSeed(
seed: seed,
Expand All @@ -167,18 +119,18 @@ bip32.BIP32 deriveChildNodeFromPath({
return node;
}

extension on BipNode {
extension on HDNode {
String toBase58wkCompatibility(int parentFingerprint, int depth) {
final version =
(!isNeutered()) ? network.bip32.private : network.bip32.public;
(!isNeutered) ? network!.bip32.private : network!.bip32.public;
Uint8List buffer = new Uint8List(78);
ByteData bytes = buffer.buffer.asByteData();
bytes.setUint32(0, version);
bytes.setUint8(4, depth);
bytes.setUint32(5, parentFingerprint);
bytes.setUint32(9, index);
buffer.setRange(13, 45, chainCode);
if (!isNeutered()) {
if (!isNeutered) {
bytes.setUint8(45, 0);
buffer.setRange(46, 78, privateKey!);
} else {
Expand All @@ -189,7 +141,7 @@ extension on BipNode {
}
}

BipNode deriveNode(Uint8List seed, String path) {
final node = bip32.BIP32.fromSeed(seed);
HDNode deriveNode(Uint8List seed, String path) {
final node = HDNode.fromSeed(seed);
return node.derivePath(path);
}
13 changes: 7 additions & 6 deletions lib/src/crypto/utxo/utils/eurocoin_signing.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:bip32/bip32.dart' as bip32;
import 'package:hex/hex.dart';
import 'package:walletkit_dart/src/crypto/utxo/utils/ecurve.dart';
import 'package:walletkit_dart/src/crypto/utxo/utils/pubkey_to_address.dart';
import 'package:walletkit_dart/src/domain/exceptions.dart';
import 'package:walletkit_dart/src/utils/crypto.dart';
import 'package:walletkit_dart/src/wallet/hd_node.dart';
import 'package:walletkit_dart/walletkit_dart.dart';
import 'package:pointycastle/src/utils.dart' as p_utils;
import 'package:bip32/src/utils/ecurve.dart' as ecc;
import 'package:convert/convert.dart' as convert;

List<int> nomoIdDerivationPath(int hostId) {
Expand Down Expand Up @@ -102,11 +103,11 @@ Future<NodeWithAddress> deriveBIP32Ec8({
}) async {
final String derivationPathString = _derivationPathToString(derivationPath);

bip32.BIP32 seedNode = bip32.BIP32.fromSeed(seed);
final bip32.BIP32 childNode = seedNode.derivePath(derivationPathString);
final masterNode = HDNode.fromSeed(seed);
final childNode = masterNode.derivePath(derivationPathString);

const compressed = false; // needed for address backwards compat
final publicKey = ecc.pointFromScalar(childNode.privateKey!, compressed)!;
final publicKey = pointFromScalar(childNode.privateKey!, compressed)!;
final address = pubKeyToAddress(
publicKey,
AddressType.legacy,
Expand All @@ -116,7 +117,7 @@ Future<NodeWithAddress> deriveBIP32Ec8({
return ChangeNode(
bip32Node: childNode,
address: address,
derivationPath: "", //
derivationPath: derivationPathString,
addresses: {AddressType.legacy: address},
walletPurpose: HDWalletPurpose.BIP44,
publicKey: childNode.publicKey.toHex,
Expand Down
Loading
Loading