diff --git a/lib/wallets/crypto_currency/coins/namecoin.dart b/lib/wallets/crypto_currency/coins/namecoin.dart index 486901964..cfe36d46b 100644 --- a/lib/wallets/crypto_currency/coins/namecoin.dart +++ b/lib/wallets/crypto_currency/coins/namecoin.dart @@ -19,18 +19,43 @@ class Namecoin extends Bip39HDCurrency { } @override - // TODO: implement minConfirms - int get minConfirms => throw UnimplementedError(); + // See https://github.com/cypherstack/stack_wallet/blob/621aff47969761014e0a6c4e699cb637d5687ab3/lib/services/coins/namecoin/namecoin_wallet.dart#L58 + int get minConfirms => 2; @override + // See https://github.com/cypherstack/stack_wallet/blob/621aff47969761014e0a6c4e699cb637d5687ab3/lib/services/coins/namecoin/namecoin_wallet.dart#L80 String constructDerivePath({ required DerivePathType derivePathType, int account = 0, required int chain, required int index, }) { - // TODO: implement constructDerivePath - throw UnimplementedError(); + String coinType; + switch (networkParams.wifPrefix) { + case 0xb4: // NMC mainnet wif. + coinType = "7"; // NMC mainnet. + break; + // TODO: [prio=low] Add testnet support. + default: + throw Exception("Invalid Namecoin network wif used!"); + } + + int purpose; + switch (derivePathType) { + case DerivePathType.bip44: + purpose = 44; + break; + case DerivePathType.bip49: + purpose = 49; + break; + case DerivePathType.bip84: + purpose = 84; + break; + default: + throw Exception("DerivePathType $derivePathType not supported"); + } + + return "m/$purpose'/$coinType'/$account'/$chain/$index"; } @override @@ -48,40 +73,97 @@ class Namecoin extends Bip39HDCurrency { isFailover: true, isDown: false, ); - + // case CryptoCurrencyNetwork.test: + // TODO: [prio=low] Add testnet support. default: throw UnimplementedError(); } } @override - // TODO: implement dustLimit - Amount get dustLimit => throw UnimplementedError(); + // See https://github.com/cypherstack/stack_wallet/blob/621aff47969761014e0a6c4e699cb637d5687ab3/lib/services/coins/namecoin/namecoin_wallet.dart#L60 + Amount get dustLimit => + Amount(rawValue: BigInt.from(546), fractionDigits: Coin.particl.decimals); @override - // TODO: implement genesisHash - String get genesisHash => throw UnimplementedError(); + // See https://github.com/cypherstack/stack_wallet/blob/621aff47969761014e0a6c4e699cb637d5687ab3/lib/services/coins/namecoin/namecoin_wallet.dart#L6 + String get genesisHash { + switch (network) { + case CryptoCurrencyNetwork.main: + return "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770"; + case CryptoCurrencyNetwork.test: + return "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"; + default: + throw Exception("Unsupported network: $network"); + } + } @override ({coinlib.Address address, AddressType addressType}) getAddressForPublicKey( {required coinlib.ECPublicKey publicKey, required DerivePathType derivePathType}) { - // TODO: implement getAddressForPublicKey - throw UnimplementedError(); + switch (derivePathType) { + // case DerivePathType.bip16: // TODO: [prio=low] Add P2SH support. + + case DerivePathType.bip44: + final addr = coinlib.P2PKHAddress.fromPublicKey( + publicKey, + version: networkParams.p2pkhPrefix, + ); + + return (address: addr, addressType: AddressType.p2pkh); + + // case DerivePathType.bip49: + + case DerivePathType.bip84: + final addr = coinlib.P2WPKHAddress.fromPublicKey( + publicKey, + hrp: networkParams.bech32Hrp, + ); + + return (address: addr, addressType: AddressType.p2wpkh); + + default: + throw Exception("DerivePathType $derivePathType not supported"); + } } @override - // TODO: implement networkParams - coinlib.NetworkParams get networkParams => throw UnimplementedError(); + // See https://github.com/cypherstack/stack_wallet/blob/621aff47969761014e0a6c4e699cb637d5687ab3/lib/services/coins/namecoin/namecoin_wallet.dart#L3474 + coinlib.NetworkParams get networkParams { + switch (network) { + case CryptoCurrencyNetwork.main: + return const coinlib.NetworkParams( + wifPrefix: 0xb4, // From 180. + p2pkhPrefix: 0x34, // From 52. + p2shPrefix: 0x0d, // From 13. + privHDPrefix: 0x0488ade4, + pubHDPrefix: 0x0488b21e, + bech32Hrp: "nc", + messagePrefix: '\x18Namecoin Signed Message:\n', + ); + // case CryptoCurrencyNetwork.test: + // TODO: [prio=low] Add testnet support. + default: + throw Exception("Unsupported network: $network"); + } + } @override - // TODO: implement supportedDerivationPathTypes - List get supportedDerivationPathTypes => - throw UnimplementedError(); + List get supportedDerivationPathTypes => [ + // DerivePathType.bip16, // TODO: [prio=low] Add P2SH support. + DerivePathType.bip44, + // DerivePathType.bip49, + DerivePathType.bip84, + ]; @override bool validateAddress(String address) { - // TODO: implement validateAddress - throw UnimplementedError(); + try { + coinlib.Address.fromString(address, networkParams); + return true; + } catch (_) { + return false; + } } } diff --git a/lib/wallets/wallet/impl/namecoin_wallet.dart b/lib/wallets/wallet/impl/namecoin_wallet.dart index 7ee1d233f..f1358f15b 100644 --- a/lib/wallets/wallet/impl/namecoin_wallet.dart +++ b/lib/wallets/wallet/impl/namecoin_wallet.dart @@ -6,6 +6,7 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; +import 'package:tuple/tuple.dart'; class NamecoinWallet extends Bip39HDWallet with ElectrumXInterface, CoinControlInterface { @@ -46,26 +47,45 @@ class NamecoinWallet extends Bip39HDWallet @override Future<({bool blocked, String? blockedReason, String? utxoLabel})> checkBlockUTXO(Map jsonUTXO, String? scriptPubKeyHex, - Map jsonTX, String? utxoOwnerAddress) { - // TODO: implement checkBlockUTXO - throw UnimplementedError(); + Map jsonTX, String? utxoOwnerAddress) async { + // Namecoin doesn't have special outputs like tokens, ordinals, etc. + return (blocked: false, blockedReason: null, utxoLabel: null); } @override int estimateTxFee({required int vSize, required int feeRatePerKB}) { - // TODO: implement estimateTxFee - throw UnimplementedError(); + return vSize * (feeRatePerKB / 1000).ceil(); } + // TODO: Check if this is the correct formula for namecoin. @override Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { - // TODO: implement roughFeeEstimate - throw UnimplementedError(); + return Amount( + rawValue: BigInt.from( + ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * + (feeRatePerKB / 1000).ceil()), + fractionDigits: cryptoCurrency.fractionDigits, + ); } @override - Future updateTransactions() { - // TODO: implement updateTransactions - throw UnimplementedError(); + Future updateTransactions() async { + final currentChainHeight = await fetchChainHeight(); + + // TODO: [prio=low] switch to V2 transactions. + final data = await fetchTransactionsV1( + addresses: await fetchAddressesForElectrumXScan(), + currentChainHeight: currentChainHeight, + ); + + await mainDB.addNewTransactionData( + data + .map((e) => Tuple2( + e.transaction, + e.address, + )) + .toList(), + walletId, + ); } }