import 'dart:convert'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/address_info.dart'; import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_decred/api/libdcrwallet.dart'; part 'wallet_addresses.g.dart'; class DecredWalletAddresses = DecredWalletAddressesBase with _$DecredWalletAddresses; abstract class DecredWalletAddressesBase extends WalletAddresses with Store { DecredWalletAddressesBase(WalletInfo walletInfo, Libwallet libwallet) : _libwallet = libwallet, super(walletInfo); final Libwallet _libwallet; String currentAddr = ''; @observable bool isEnabledAutoGenerateSubaddress = true; @observable String selectedAddr = ''; @override @computed String get address { return selectedAddr; } @override set address(value) { selectedAddr = value; } @override Future init() async { if (walletInfo.addresses != null) { addressesMap = walletInfo.addresses!; } if (walletInfo.addressInfos != null) { addressInfos = walletInfo.addressInfos!; } if (walletInfo.usedAddresses != null) { usedAddresses = {...walletInfo.usedAddresses!}; } await updateAddressesInBox(); } @override Future updateAddressesInBox() async { final addrs = await libAddresses(); final allAddrs = new List.from(addrs.usedAddrs)..addAll(addrs.unusedAddrs); // Add all addresses. allAddrs.forEach((addr) { if (addressesMap.containsKey(addr)) { return; } addressesMap[addr] = ""; addressInfos[0] ??= []; addressInfos[0]?.add(AddressInfo(address: addr, label: "", accountIndex: 0)); }); // Add used addresses. addrs.usedAddrs.forEach((addr) { if (!usedAddresses.contains(addr)) { usedAddresses.add(addr); } }); if (addrs.unusedAddrs.length > 0 && addrs.unusedAddrs[0] != currentAddr) { currentAddr = addrs.unusedAddrs[0]; selectedAddr = currentAddr; } await saveAddressesInBox(); } List getAddressInfos() { if (addressInfos.containsKey(0)) { return addressInfos[0]!; } return []; } Future updateAddress(String address, String label) async { if (!addressInfos.containsKey(0)) { return; } addressInfos[0]!.forEach((info) { if (info.address == address) { info.label = label; } }); await saveAddressesInBox(); } Future libAddresses() async { final nUsed = "10"; var nUnused = "1"; if (this.isEnabledAutoGenerateSubaddress) { nUnused = "3"; } try { final res = await _libwallet.addresses(walletInfo.name, nUsed, nUnused); final decoded = json.decode(res); final usedAddrs = List.from(decoded["used"] ?? []); final unusedAddrs = List.from(decoded["unused"] ?? []); // index is the index of the first unused address. final index = decoded["index"] ?? 0; return new LibAddresses(usedAddrs, unusedAddrs, index); } catch (e) { printV(e); return LibAddresses([], [], 0); } } Future generateNewAddress(String label) async { // NOTE: This will ignore the gap limit and may cause problems when restoring from seed if too // many addresses are taken and not used. final addr = await _libwallet.newExternalAddress(walletInfo.name) ?? ''; if (addr == "") { return; } if (!addressesMap.containsKey(addr)) { addressesMap[addr] = ""; addressInfos[0] ??= []; addressInfos[0]?.add(AddressInfo(address: addr, label: label, accountIndex: 0)); } selectedAddr = addr; await saveAddressesInBox(); } } class LibAddresses { final List usedAddrs, unusedAddrs; final int firstUnusedAddrIndex; LibAddresses(this.usedAddrs, this.unusedAddrs, this.firstUnusedAddrIndex); }