Merge branch 'staging' into add_frost

This commit is contained in:
sneurlax 2024-03-01 13:07:04 -06:00
commit 5644d763c2
10 changed files with 481 additions and 425 deletions

View file

@ -56,6 +56,7 @@ import 'package:stackwallet/wallets/wallet/impl/monero_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/wownero_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/wownero_wallet.dart';
import 'package:stackwallet/wallets/wallet/supporting/epiccash_wallet_info_extension.dart'; import 'package:stackwallet/wallets/wallet/supporting/epiccash_wallet_info_extension.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart'; import 'package:stackwallet/wallets/wallet/wallet.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
@ -723,353 +724,376 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
], ],
), ),
body: Container( body: Container(
color: Theme.of(context).extension<StackColors>()!.background, color: Theme.of(context).extension<StackColors>()!.background,
child: Padding( child: Padding(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( controller: controller,
children: [ child: Column(
/*if (isDesktop) children: [
/*if (isDesktop)
const Spacer( const Spacer(
flex: 10, flex: 10,
),*/ ),*/
if (!isDesktop) if (!isDesktop)
Text( Text(
widget.walletName, widget.walletName,
style: STextStyles.itemSubtitle(context), style: STextStyles.itemSubtitle(context),
),
SizedBox(
height: isDesktop ? 0 : 4,
),
Text(
"Recovery phrase",
style: isDesktop
? STextStyles.desktopH2(context)
: STextStyles.pageTitleH1(context),
),
SizedBox(
height: isDesktop ? 16 : 8,
),
Text(
"Enter your $_seedWordCount-word recovery phrase.",
style: isDesktop
? STextStyles.desktopSubtitleH2(context)
: STextStyles.subtitle(context),
),
SizedBox(
height: isDesktop ? 16 : 10,
),
if (isDesktop)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: pasteMnemonic,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 12,
),
child: Row(
children: [
SvgPicture.asset(
Assets.svg.clipboard,
width: 22,
height: 22,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
),
const SizedBox(
width: 8,
),
Text(
"Paste",
style: STextStyles
.desktopButtonSmallSecondaryEnabled(context),
)
],
),
),
), ),
], SizedBox(
), height: isDesktop ? 0 : 4,
if (isDesktop)
const SizedBox(
height: 20,
),
if (isDesktop)
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 1008,
), ),
child: Builder( Text(
builder: (BuildContext context) { "Recovery phrase",
const cols = 4; style: isDesktop
final int rows = _seedWordCount ~/ cols; ? STextStyles.desktopH2(context)
final int remainder = _seedWordCount % cols; : STextStyles.pageTitleH1(context),
),
return Column( SizedBox(
children: [ height: isDesktop ? 16 : 8,
Form( ),
key: _formKey, Text(
child: TableView( "Enter your $_seedWordCount-word recovery phrase.",
shrinkWrap: true, style: isDesktop
rowSpacing: 20, ? STextStyles.desktopSubtitleH2(context)
rows: [ : STextStyles.subtitle(context),
for (int i = 0; i < rows; i++) ),
TableViewRow( SizedBox(
crossAxisAlignment: height: isDesktop ? 16 : 10,
CrossAxisAlignment.start, ),
spacing: 16, if (isDesktop)
cells: [ Row(
for (int j = 1; j <= cols; j++) mainAxisAlignment: MainAxisAlignment.center,
TableViewCell( children: [
flex: 1, TextButton(
child: Column( onPressed: pasteMnemonic,
children: [ child: Padding(
TextFormField( padding: const EdgeInsets.symmetric(
autocorrect: !isDesktop, horizontal: 16.0,
enableSuggestions: !isDesktop, vertical: 12,
textCapitalization: ),
TextCapitalization.none, child: Row(
key: Key( children: [
"restoreMnemonicFormField_$i"), SvgPicture.asset(
decoration: Assets.svg.clipboard,
_getInputDecorationFor( width: 22,
_inputStatuses[ height: 22,
i * 4 + j - 1], color: Theme.of(context)
"${i * 4 + j}"), .extension<StackColors>()!
autovalidateMode: .buttonTextSecondary,
AutovalidateMode ),
.onUserInteraction, const SizedBox(
selectionControls: width: 8,
i * 4 + j - 1 == 1 ),
? textSelectionControls Text(
: null, "Paste",
// focusNode: style: STextStyles
// _focusNodes[i * 4 + j - 1], .desktopButtonSmallSecondaryEnabled(
onChanged: (value) { context),
final FormInputStatus )
formInputStatus;
if (value.isEmpty) {
formInputStatus =
FormInputStatus.empty;
} else if (_isValidMnemonicWord(
value
.trim()
.toLowerCase())) {
formInputStatus =
FormInputStatus.valid;
} else {
formInputStatus =
FormInputStatus.invalid;
}
// if (formInputStatus ==
// FormInputStatus.valid) {
// if (i * 4 + j <
// _focusNodes.length) {
// _focusNodes[i * 4 + j]
// .requestFocus();
// } else if (i * 4 + j ==
// _focusNodes.length) {
// _focusNodes[i * 4 + j - 1]
// .unfocus();
// }
// }
setState(() {
_inputStatuses[i * 4 +
j -
1] = formInputStatus;
});
},
controller:
_controllers[i * 4 + j - 1],
style:
STextStyles.field(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textRestore,
fontSize: isDesktop ? 16 : 14,
),
),
if (_inputStatuses[
i * 4 + j - 1] ==
FormInputStatus.invalid)
Align(
alignment: Alignment.topLeft,
child: Padding(
padding:
const EdgeInsets.only(
left: 12.0,
bottom: 4.0,
),
child: Text(
"Please check spelling",
textAlign: TextAlign.left,
style: STextStyles.label(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.textError,
),
),
),
)
],
),
),
],
expandingChild: null,
),
if (remainder > 0)
TableViewRow(
spacing: 16,
cells: [
for (int i = rows * cols;
i < _seedWordCount;
i++) ...[
TableViewCell(
flex: 1,
child: Column(
children: [
TextFormField(
autocorrect: !isDesktop,
enableSuggestions: !isDesktop,
textCapitalization:
TextCapitalization.none,
key: Key(
"restoreMnemonicFormField_$i"),
decoration:
_getInputDecorationFor(
_inputStatuses[i],
"${i + 1}"),
autovalidateMode:
AutovalidateMode
.onUserInteraction,
selectionControls: i == 1
? textSelectionControls
: null,
// focusNode: _focusNodes[i],
onChanged: (value) {
final FormInputStatus
formInputStatus;
if (value.isEmpty) {
formInputStatus =
FormInputStatus.empty;
} else if (_isValidMnemonicWord(
value
.trim()
.toLowerCase())) {
formInputStatus =
FormInputStatus.valid;
} else {
formInputStatus =
FormInputStatus.invalid;
}
// if (formInputStatus ==
// FormInputStatus
// .valid &&
// (i - 1) <
// _focusNodes.length) {
// Focus.of(context)
// .requestFocus(
// _focusNodes[i]);
// }
// if (formInputStatus ==
// FormInputStatus.valid) {
// if (i + 1 <
// _focusNodes.length) {
// _focusNodes[i + 1]
// .requestFocus();
// } else if (i + 1 ==
// _focusNodes.length) {
// _focusNodes[i].unfocus();
// }
// }
},
controller: _controllers[i],
style:
STextStyles.field(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.overlay,
fontSize: isDesktop ? 16 : 14,
),
),
if (_inputStatuses[i] ==
FormInputStatus.invalid)
Align(
alignment: Alignment.topLeft,
child: Padding(
padding:
const EdgeInsets.only(
left: 12.0,
bottom: 4.0,
),
child: Text(
"Please check spelling",
textAlign: TextAlign.left,
style: STextStyles.label(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.textError,
),
),
),
)
],
),
),
],
for (int i = remainder;
i < cols;
i++) ...[
TableViewCell(
flex: 1,
child: Container(),
),
],
],
expandingChild: null,
),
], ],
), ),
), ),
const SizedBox( ),
height: 32, ],
), ),
PrimaryButton( if (isDesktop)
label: "Restore wallet", const SizedBox(
width: 480, height: 20,
onPressed: requestRestore, ),
), if (isDesktop)
], ConstrainedBox(
); constraints: const BoxConstraints(
}, maxWidth: 1008,
), ),
), child: Builder(
/*if (isDesktop) builder: (BuildContext context) {
const cols = 4;
final int rows = _seedWordCount ~/ cols;
final int remainder = _seedWordCount % cols;
return Column(
children: [
Form(
key: _formKey,
child: TableView(
shrinkWrap: true,
rowSpacing: 20,
rows: [
for (int i = 0; i < rows; i++)
TableViewRow(
crossAxisAlignment:
CrossAxisAlignment.start,
spacing: 16,
cells: [
for (int j = 1; j <= cols; j++)
TableViewCell(
flex: 1,
child: Column(
children: [
TextFormField(
autocorrect: !isDesktop,
enableSuggestions:
!isDesktop,
textCapitalization:
TextCapitalization.none,
key: Key(
"restoreMnemonicFormField_$i"),
decoration:
_getInputDecorationFor(
_inputStatuses[
i * 4 + j - 1],
"${i * 4 + j}"),
autovalidateMode:
AutovalidateMode
.onUserInteraction,
selectionControls: i * 4 +
j -
1 ==
1
? textSelectionControls
: null,
// focusNode:
// _focusNodes[i * 4 + j - 1],
onChanged: (value) {
final FormInputStatus
formInputStatus;
if (value.isEmpty) {
formInputStatus =
FormInputStatus
.empty;
} else if (_isValidMnemonicWord(
value
.trim()
.toLowerCase())) {
formInputStatus =
FormInputStatus
.valid;
} else {
formInputStatus =
FormInputStatus
.invalid;
}
// if (formInputStatus ==
// FormInputStatus.valid) {
// if (i * 4 + j <
// _focusNodes.length) {
// _focusNodes[i * 4 + j]
// .requestFocus();
// } else if (i * 4 + j ==
// _focusNodes.length) {
// _focusNodes[i * 4 + j - 1]
// .unfocus();
// }
// }
setState(() {
_inputStatuses[
i * 4 + j - 1] =
formInputStatus;
});
},
controller: _controllers[
i * 4 + j - 1],
style: STextStyles.field(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.textRestore,
fontSize:
isDesktop ? 16 : 14,
),
),
if (_inputStatuses[
i * 4 + j - 1] ==
FormInputStatus.invalid)
Align(
alignment:
Alignment.topLeft,
child: Padding(
padding:
const EdgeInsets
.only(
left: 12.0,
bottom: 4.0,
),
child: Text(
"Please check spelling",
textAlign:
TextAlign.left,
style:
STextStyles.label(
context)
.copyWith(
color: Theme.of(
context)
.extension<
StackColors>()!
.textError,
),
),
),
)
],
),
),
],
expandingChild: null,
),
if (remainder > 0)
TableViewRow(
spacing: 16,
cells: [
for (int i = rows * cols;
i < _seedWordCount;
i++) ...[
TableViewCell(
flex: 1,
child: Column(
children: [
TextFormField(
autocorrect: !isDesktop,
enableSuggestions:
!isDesktop,
textCapitalization:
TextCapitalization.none,
key: Key(
"restoreMnemonicFormField_$i"),
decoration:
_getInputDecorationFor(
_inputStatuses[i],
"${i + 1}"),
autovalidateMode:
AutovalidateMode
.onUserInteraction,
selectionControls: i == 1
? textSelectionControls
: null,
// focusNode: _focusNodes[i],
onChanged: (value) {
final FormInputStatus
formInputStatus;
if (value.isEmpty) {
formInputStatus =
FormInputStatus
.empty;
} else if (_isValidMnemonicWord(
value
.trim()
.toLowerCase())) {
formInputStatus =
FormInputStatus
.valid;
} else {
formInputStatus =
FormInputStatus
.invalid;
}
// if (formInputStatus ==
// FormInputStatus
// .valid &&
// (i - 1) <
// _focusNodes.length) {
// Focus.of(context)
// .requestFocus(
// _focusNodes[i]);
// }
// if (formInputStatus ==
// FormInputStatus.valid) {
// if (i + 1 <
// _focusNodes.length) {
// _focusNodes[i + 1]
// .requestFocus();
// } else if (i + 1 ==
// _focusNodes.length) {
// _focusNodes[i].unfocus();
// }
// }
},
controller: _controllers[i],
style: STextStyles.field(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.overlay,
fontSize:
isDesktop ? 16 : 14,
),
),
if (_inputStatuses[i] ==
FormInputStatus.invalid)
Align(
alignment:
Alignment.topLeft,
child: Padding(
padding:
const EdgeInsets
.only(
left: 12.0,
bottom: 4.0,
),
child: Text(
"Please check spelling",
textAlign:
TextAlign.left,
style:
STextStyles.label(
context)
.copyWith(
color: Theme.of(
context)
.extension<
StackColors>()!
.textError,
),
),
),
)
],
),
),
],
for (int i = remainder;
i < cols;
i++) ...[
TableViewCell(
flex: 1,
child: Container(),
),
],
],
expandingChild: null,
),
],
),
),
const SizedBox(
height: 32,
),
PrimaryButton(
label: "Restore wallet",
width: 480,
onPressed: requestRestore,
),
],
);
},
),
),
/*if (isDesktop)
const Spacer( const Spacer(
flex: 15, flex: 15,
),*/ ),*/
if (!isDesktop) if (!isDesktop)
Expanded( Padding(
child: SingleChildScrollView(
controller: controller,
child: Padding(
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: Form( child: Form(
key: _formKey, key: _formKey,
@ -1169,13 +1193,11 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
), ),
), ),
), ),
), ],
), ),
], ),
), ),
), ),
), );
),
);
} }
} }

View file

@ -1048,7 +1048,7 @@ class _TransactionV2DetailsViewState
pTransactionNote( pTransactionNote(
( (
txid: (coin == Coin.epicCash) ? txid: (coin == Coin.epicCash) ?
_transaction.slateId as String _transaction.slateId.toString()
: _transaction.txid, : _transaction.txid,
walletId: walletId walletId: walletId
), ),

View file

@ -612,7 +612,7 @@ abstract class EthereumAPI {
final response = await client.get( final response = await client.get(
url: Uri.parse( url: Uri.parse(
// "$stackBaseServer/tokens?addrs=$contractAddress&parts=all", // "$stackBaseServer/tokens?addrs=$contractAddress&parts=all",
"$stackBaseServer/names?terms=$contractAddress", "$stackBaseServer/names?terms=$contractAddress&all",
), ),
proxyInfo: Prefs.instance.useTor proxyInfo: Prefs.instance.useTor
? TorService.sharedInstance.getProxyInfo() ? TorService.sharedInstance.getProxyInfo()

View file

@ -17,7 +17,7 @@ abstract class DefaultEpicBoxes {
static List<String> get defaultIds => ['americas', 'asia', 'europe']; static List<String> get defaultIds => ['americas', 'asia', 'europe'];
static EpicBoxServerModel get americas => EpicBoxServerModel( static EpicBoxServerModel get americas => EpicBoxServerModel(
host: 'stackwallet.epicbox.com', host: 'epicbox.stackwallet.com',
port: 443, port: 443,
name: 'Americas', name: 'Americas',
id: 'americas', id: 'americas',

View file

@ -185,7 +185,8 @@ class Ecash extends Bip39HDCurrency {
addr = cashAddr.split(":").last; addr = cashAddr.split(":").last;
} }
return addr.startsWith("q") || addr.startsWith("p"); return addr.startsWith("q") /*|| addr.startsWith("p")*/;
// Do not validate "p" (P2SH) addresses.
} }
@override @override

View file

@ -5,6 +5,7 @@ import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/wallets/isar/models/spark_coin.dart';
import 'package:tezart/tezart.dart' as tezart; import 'package:tezart/tezart.dart' as tezart;
import 'package:web3dart/web3dart.dart' as web3dart; import 'package:web3dart/web3dart.dart' as web3dart;
@ -69,6 +70,7 @@ class TxData {
bool isChange, bool isChange,
})>? sparkRecipients; })>? sparkRecipients;
final List<TxData>? sparkMints; final List<TxData>? sparkMints;
final List<SparkCoin>? usedSparkCoins;
final TransactionV2? tempTx; final TransactionV2? tempTx;
@ -105,6 +107,7 @@ class TxData {
this.tezosOperationsList, this.tezosOperationsList,
this.sparkRecipients, this.sparkRecipients,
this.sparkMints, this.sparkMints,
this.usedSparkCoins,
this.tempTx, this.tempTx,
}); });
@ -187,6 +190,7 @@ class TxData {
})>? })>?
sparkRecipients, sparkRecipients,
List<TxData>? sparkMints, List<TxData>? sparkMints,
List<SparkCoin>? usedSparkCoins,
TransactionV2? tempTx, TransactionV2? tempTx,
}) { }) {
return TxData( return TxData(
@ -224,6 +228,7 @@ class TxData {
tezosOperationsList: tezosOperationsList ?? this.tezosOperationsList, tezosOperationsList: tezosOperationsList ?? this.tezosOperationsList,
sparkRecipients: sparkRecipients ?? this.sparkRecipients, sparkRecipients: sparkRecipients ?? this.sparkRecipients,
sparkMints: sparkMints ?? this.sparkMints, sparkMints: sparkMints ?? this.sparkMints,
usedSparkCoins: usedSparkCoins ?? this.usedSparkCoins,
tempTx: tempTx ?? this.tempTx, tempTx: tempTx ?? this.tempTx,
); );
} }
@ -262,6 +267,7 @@ class TxData {
'tezosOperationsList: $tezosOperationsList, ' 'tezosOperationsList: $tezosOperationsList, '
'sparkRecipients: $sparkRecipients, ' 'sparkRecipients: $sparkRecipients, '
'sparkMints: $sparkMints, ' 'sparkMints: $sparkMints, '
'usedSparkCoins: $usedSparkCoins, '
'tempTx: $tempTx, ' 'tempTx: $tempTx, '
'}'; '}';
} }

View file

@ -103,46 +103,30 @@ class EpiccashWallet extends Bip39Wallet {
} }
Future<EpicBoxConfigModel> getEpicBoxConfig() async { Future<EpicBoxConfigModel> getEpicBoxConfig() async {
EpicBoxConfigModel? _epicBoxConfig; EpicBoxConfigModel? _epicBoxConfig =
// read epicbox config from secure store EpicBoxConfigModel.fromServer(DefaultEpicBoxes.defaultEpicBoxServer);
String? storedConfig =
await secureStorageInterface.read(key: '${walletId}_epicboxConfig');
// we should move to storing the primary server model like we do with nodes, and build the config from that (see epic-mobile) //Get the default Epicbox server and check if it's conected
// EpicBoxServerModel? _epicBox = epicBox ?? // bool isEpicboxConnected = await _testEpicboxServer(
// DB.instance.get<EpicBoxServerModel>( // DefaultEpicBoxes.defaultEpicBoxServer.host, DefaultEpicBoxes.defaultEpicBoxServer.port ?? 443);
// boxName: DB.boxNamePrimaryEpicBox, key: 'primary');
// Logging.instance.log(
// "Read primary Epic Box config: ${jsonEncode(_epicBox)}",
// level: LogLevel.Info);
if (storedConfig == null) { // if (isEpicboxConnected) {
// if no config stored, use the default epicbox server as config //Use default server for as Epicbox config
_epicBoxConfig =
EpicBoxConfigModel.fromServer(DefaultEpicBoxes.defaultEpicBoxServer);
} else {
// if a config is stored, test it
_epicBoxConfig = EpicBoxConfigModel.fromString( // }
storedConfig); // fromString handles checking old config formats // else {
} // //Use Europe config
// _epicBoxConfig = EpicBoxConfigModel.fromServer(DefaultEpicBoxes.europe);
bool isEpicboxConnected = await _testEpicboxServer( // }
_epicBoxConfig.host, _epicBoxConfig.port ?? 443); // // example of selecting another random server from the default list
// // alternative servers: copy list of all default EB servers but remove the default default
if (!isEpicboxConnected) { // // List<EpicBoxServerModel> alternativeServers = DefaultEpicBoxes.all;
// default Epicbox is not connected, default to Europe // // alternativeServers.removeWhere((opt) => opt.name == DefaultEpicBoxes.defaultEpicBoxServer.name);
_epicBoxConfig = EpicBoxConfigModel.fromServer(DefaultEpicBoxes.europe); // // alternativeServers.shuffle(); // randomize which server is used
// // _epicBoxConfig = EpicBoxConfigModel.fromServer(alternativeServers.first);
// example of selecting another random server from the default list //
// alternative servers: copy list of all default EB servers but remove the default default // // TODO test this connection before returning it
// List<EpicBoxServerModel> alternativeServers = DefaultEpicBoxes.all; // }
// alternativeServers.removeWhere((opt) => opt.name == DefaultEpicBoxes.defaultEpicBoxServer.name);
// alternativeServers.shuffle(); // randomize which server is used
// _epicBoxConfig = EpicBoxConfigModel.fromServer(alternativeServers.first);
// TODO test this connection before returning it
}
return _epicBoxConfig; return _epicBoxConfig;
} }
@ -334,36 +318,50 @@ class EpiccashWallet extends Bip39Wallet {
int index, int index,
) async { ) async {
Address? address = await getCurrentReceivingAddress(); Address? address = await getCurrentReceivingAddress();
EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig();
if (address == null) { if (address != null) {
final wallet = final splitted = address.value.split('@');
await secureStorageInterface.read(key: '${walletId}_wallet'); //Check if the address is the same as the current epicbox domain
EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig(); //Since we're only using one epicbpox now this doesn't apply but will be
// useful in the future
final walletAddress = await epiccash.LibEpiccash.getAddressInfo( if (splitted[1] != epicboxConfig.host) {
wallet: wallet!, //Update the address
index: index, address = await thisWalletAddress(index, epicboxConfig);
epicboxConfig: epicboxConfig.toString(), }
); } else {
address = await thisWalletAddress(index, epicboxConfig);
Logging.instance.log(
"WALLET_ADDRESS_IS $walletAddress",
level: LogLevel.Info,
);
address = Address(
walletId: walletId,
value: walletAddress,
derivationIndex: index,
derivationPath: null,
type: AddressType.mimbleWimble,
subType: AddressSubType.receiving,
publicKey: [], // ??
);
await mainDB.updateOrPutAddresses([address]);
} }
return address;
}
Future<Address> thisWalletAddress(int index, EpicBoxConfigModel epicboxConfig) async {
final wallet =
await secureStorageInterface.read(key: '${walletId}_wallet');
// EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig();
final walletAddress = await epiccash.LibEpiccash.getAddressInfo(
wallet: wallet!,
index: index,
epicboxConfig: epicboxConfig.toString(),
);
Logging.instance.log(
"WALLET_ADDRESS_IS $walletAddress",
level: LogLevel.Info,
);
final address = Address(
walletId: walletId,
value: walletAddress,
derivationIndex: index,
derivationPath: null,
type: AddressType.mimbleWimble,
subType: AddressSubType.receiving,
publicKey: [], // ??
);
await mainDB.updateOrPutAddresses([address]);
return address; return address;
} }
@ -936,6 +934,7 @@ class EpiccashWallet extends Bip39Wallet {
.findAll(); .findAll();
final myAddressesSet = myAddresses.toSet(); final myAddressesSet = myAddresses.toSet();
final transactions = await epiccash.LibEpiccash.getTransactions( final transactions = await epiccash.LibEpiccash.getTransactions(
wallet: wallet!, wallet: wallet!,
refreshFromNode: refreshFromNode, refreshFromNode: refreshFromNode,

View file

@ -499,6 +499,27 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
), ),
); );
final List<SparkCoin> usedSparkCoins = [];
for (final usedCoin in spend.usedCoins) {
try {
usedSparkCoins.add(coins
.firstWhere((e) =>
usedCoin.height == e.height &&
usedCoin.groupId == e.groupId &&
base64Decode(e.serializedCoinB64!)
.toHex
.startsWith(base64Decode(usedCoin.serializedCoin).toHex))
.copyWith(
isUsed: true,
));
} catch (_) {
throw Exception(
"Unexpectedly did not find used spark coin. This should never happen.",
);
}
}
return txData.copyWith( return txData.copyWith(
raw: rawTxHex, raw: rawTxHex,
vSize: extractedTx.virtualSize(), vSize: extractedTx.virtualSize(),
@ -523,7 +544,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
height: null, height: null,
version: 3, version: 3,
), ),
// TODO used coins usedSparkCoins: usedSparkCoins,
); );
} }
@ -540,17 +561,17 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
Logging.instance.log("Sent txHash: $txHash", level: LogLevel.Info); Logging.instance.log("Sent txHash: $txHash", level: LogLevel.Info);
txData = txData.copyWith( txData = txData.copyWith(
// TODO mark spark coins as spent locally and update balance before waiting to check via electrumx?
// usedUTXOs:
// txData.usedUTXOs!.map((e) => e.copyWith(used: true)).toList(),
// TODO revisit setting these both // TODO revisit setting these both
txHash: txHash, txHash: txHash,
txid: txHash, txid: txHash,
); );
// // mark utxos as used
// await mainDB.putUTXOs(txData.usedUTXOs!); // Update used spark coins as used in database. They should already have
// been marked as isUsed.
// TODO: [prio=med] Could (probably should) throw an exception here if txData.usedSparkCoins is null or empty
if (txData.usedSparkCoins != null && txData.usedSparkCoins!.isNotEmpty) {
await _addOrUpdateSparkCoins(txData.usedSparkCoins!);
}
return await updateSentCachedTxData(txData: txData); return await updateSentCachedTxData(txData: txData);
} catch (e, s) { } catch (e, s) {
@ -1499,6 +1520,13 @@ Future<
Uint8List serializedSpendPayload, Uint8List serializedSpendPayload,
List<Uint8List> outputScripts, List<Uint8List> outputScripts,
int fee, int fee,
List<
({
int groupId,
int height,
String serializedCoin,
String serializedCoinContext
})> usedCoins,
})> _createSparkSend( })> _createSparkSend(
({ ({
String privateKeyHex, String privateKeyHex,

View file

@ -528,8 +528,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "0a34f7f48d921fb33f551cb11dfc9b2930522240" ref: "2897c6448e131241d4d91fe23fdab83305134225"
resolved-ref: "0a34f7f48d921fb33f551cb11dfc9b2930522240" resolved-ref: "2897c6448e131241d4d91fe23fdab83305134225"
url: "https://github.com/cypherstack/electrum_adapter.git" url: "https://github.com/cypherstack/electrum_adapter.git"
source: git source: git
version: "3.0.0" version: "3.0.0"
@ -674,8 +674,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: fb50031056fbea0326f7dd76ad59d165c1e5eee5 ref: "3f986ca1a94bdac5d31373454c989cc2f5842de8"
resolved-ref: fb50031056fbea0326f7dd76ad59d165c1e5eee5 resolved-ref: "3f986ca1a94bdac5d31373454c989cc2f5842de8"
url: "https://github.com/cypherstack/flutter_libsparkmobile.git" url: "https://github.com/cypherstack/flutter_libsparkmobile.git"
source: git source: git
version: "0.0.1" version: "0.0.1"

View file

@ -11,7 +11,7 @@ description: Stack Wallet
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.10.1+209 version: 1.10.2+213
environment: environment:
sdk: ">=3.0.2 <4.0.0" sdk: ">=3.0.2 <4.0.0"
@ -33,7 +33,7 @@ dependencies:
flutter_libsparkmobile: flutter_libsparkmobile:
git: git:
url: https://github.com/cypherstack/flutter_libsparkmobile.git url: https://github.com/cypherstack/flutter_libsparkmobile.git
ref: fb50031056fbea0326f7dd76ad59d165c1e5eee5 ref: 3f986ca1a94bdac5d31373454c989cc2f5842de8
flutter_libmonero: flutter_libmonero:
path: ./crypto_plugins/flutter_libmonero path: ./crypto_plugins/flutter_libmonero
@ -179,7 +179,7 @@ dependencies:
electrum_adapter: electrum_adapter:
git: git:
url: https://github.com/cypherstack/electrum_adapter.git url: https://github.com/cypherstack/electrum_adapter.git
ref: 0a34f7f48d921fb33f551cb11dfc9b2930522240 ref: 2897c6448e131241d4d91fe23fdab83305134225
stream_channel: ^2.1.0 stream_channel: ^2.1.0
dev_dependencies: dev_dependencies: