mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-23 02:54:30 +00:00
Merge remote-tracking branch 'origin/wallets_refactor' into wallets_refactor
This commit is contained in:
commit
7bf817ca66
40 changed files with 2829 additions and 1281 deletions
|
@ -22,6 +22,7 @@ import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/stack_file_system.dart';
|
import 'package:stackwallet/utilities/stack_file_system.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/spark_coin.dart';
|
import 'package:stackwallet/wallets/isar/models/spark_coin.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/models/token_wallet_info.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/wallet_info_meta.dart';
|
import 'package:stackwallet/wallets/isar/models/wallet_info_meta.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
@ -65,6 +66,7 @@ class MainDB {
|
||||||
TransactionV2Schema,
|
TransactionV2Schema,
|
||||||
SparkCoinSchema,
|
SparkCoinSchema,
|
||||||
WalletInfoMetaSchema,
|
WalletInfoMetaSchema,
|
||||||
|
TokenWalletInfoSchema,
|
||||||
],
|
],
|
||||||
directory: (await StackFileSystem.applicationIsarDirectory()).path,
|
directory: (await StackFileSystem.applicationIsarDirectory()).path,
|
||||||
// inspector: kDebugMode,
|
// inspector: kDebugMode,
|
||||||
|
|
|
@ -65,6 +65,10 @@ class TransactionV2 {
|
||||||
String? get onChainNote => _getFromOtherData(key: "onChainNote") as String?;
|
String? get onChainNote => _getFromOtherData(key: "onChainNote") as String?;
|
||||||
bool get isCancelled => _getFromOtherData(key: "isCancelled") == true;
|
bool get isCancelled => _getFromOtherData(key: "isCancelled") == true;
|
||||||
|
|
||||||
|
String? get contractAddress =>
|
||||||
|
_getFromOtherData(key: "contractAddress") as String?;
|
||||||
|
int? get nonce => _getFromOtherData(key: "nonce") as int?;
|
||||||
|
|
||||||
int getConfirmations(int currentChainHeight) {
|
int getConfirmations(int currentChainHeight) {
|
||||||
if (height == null || height! <= 0) return 0;
|
if (height == null || height! <= 0) return 0;
|
||||||
return max(0, currentChainHeight - (height! - 1));
|
return max(0, currentChainHeight - (height! - 1));
|
||||||
|
@ -76,8 +80,8 @@ class TransactionV2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
Amount getFee({required Coin coin}) {
|
Amount getFee({required Coin coin}) {
|
||||||
// try anon fee first
|
// check for override fee
|
||||||
final fee = _getAnonFee();
|
final fee = _getOverrideFee();
|
||||||
if (fee != null) {
|
if (fee != null) {
|
||||||
return fee;
|
return fee;
|
||||||
}
|
}
|
||||||
|
@ -136,10 +140,11 @@ class TransactionV2 {
|
||||||
...outputs.map((e) => e.addresses).expand((e) => e),
|
...outputs.map((e) => e.addresses).expand((e) => e),
|
||||||
};
|
};
|
||||||
|
|
||||||
Amount? _getAnonFee() {
|
Amount? _getOverrideFee() {
|
||||||
try {
|
try {
|
||||||
final map = jsonDecode(otherData!) as Map;
|
return Amount.fromSerializedJsonString(
|
||||||
return Amount.fromSerializedJsonString(map["anonFees"] as String);
|
_getFromOtherData(key: "overrideFee") as String,
|
||||||
|
);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,87 +22,97 @@ const TransactionV2Schema = CollectionSchema(
|
||||||
name: r'blockHash',
|
name: r'blockHash',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'hash': PropertySchema(
|
r'contractAddress': PropertySchema(
|
||||||
id: 1,
|
id: 1,
|
||||||
|
name: r'contractAddress',
|
||||||
|
type: IsarType.string,
|
||||||
|
),
|
||||||
|
r'hash': PropertySchema(
|
||||||
|
id: 2,
|
||||||
name: r'hash',
|
name: r'hash',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'height': PropertySchema(
|
r'height': PropertySchema(
|
||||||
id: 2,
|
id: 3,
|
||||||
name: r'height',
|
name: r'height',
|
||||||
type: IsarType.long,
|
type: IsarType.long,
|
||||||
),
|
),
|
||||||
r'inputs': PropertySchema(
|
r'inputs': PropertySchema(
|
||||||
id: 3,
|
id: 4,
|
||||||
name: r'inputs',
|
name: r'inputs',
|
||||||
type: IsarType.objectList,
|
type: IsarType.objectList,
|
||||||
target: r'InputV2',
|
target: r'InputV2',
|
||||||
),
|
),
|
||||||
r'isCancelled': PropertySchema(
|
r'isCancelled': PropertySchema(
|
||||||
id: 4,
|
id: 5,
|
||||||
name: r'isCancelled',
|
name: r'isCancelled',
|
||||||
type: IsarType.bool,
|
type: IsarType.bool,
|
||||||
),
|
),
|
||||||
r'isEpiccashTransaction': PropertySchema(
|
r'isEpiccashTransaction': PropertySchema(
|
||||||
id: 5,
|
id: 6,
|
||||||
name: r'isEpiccashTransaction',
|
name: r'isEpiccashTransaction',
|
||||||
type: IsarType.bool,
|
type: IsarType.bool,
|
||||||
),
|
),
|
||||||
|
r'nonce': PropertySchema(
|
||||||
|
id: 7,
|
||||||
|
name: r'nonce',
|
||||||
|
type: IsarType.long,
|
||||||
|
),
|
||||||
r'numberOfMessages': PropertySchema(
|
r'numberOfMessages': PropertySchema(
|
||||||
id: 6,
|
id: 8,
|
||||||
name: r'numberOfMessages',
|
name: r'numberOfMessages',
|
||||||
type: IsarType.long,
|
type: IsarType.long,
|
||||||
),
|
),
|
||||||
r'onChainNote': PropertySchema(
|
r'onChainNote': PropertySchema(
|
||||||
id: 7,
|
id: 9,
|
||||||
name: r'onChainNote',
|
name: r'onChainNote',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'otherData': PropertySchema(
|
r'otherData': PropertySchema(
|
||||||
id: 8,
|
id: 10,
|
||||||
name: r'otherData',
|
name: r'otherData',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'outputs': PropertySchema(
|
r'outputs': PropertySchema(
|
||||||
id: 9,
|
id: 11,
|
||||||
name: r'outputs',
|
name: r'outputs',
|
||||||
type: IsarType.objectList,
|
type: IsarType.objectList,
|
||||||
target: r'OutputV2',
|
target: r'OutputV2',
|
||||||
),
|
),
|
||||||
r'slateId': PropertySchema(
|
r'slateId': PropertySchema(
|
||||||
id: 10,
|
id: 12,
|
||||||
name: r'slateId',
|
name: r'slateId',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'subType': PropertySchema(
|
r'subType': PropertySchema(
|
||||||
id: 11,
|
id: 13,
|
||||||
name: r'subType',
|
name: r'subType',
|
||||||
type: IsarType.byte,
|
type: IsarType.byte,
|
||||||
enumMap: _TransactionV2subTypeEnumValueMap,
|
enumMap: _TransactionV2subTypeEnumValueMap,
|
||||||
),
|
),
|
||||||
r'timestamp': PropertySchema(
|
r'timestamp': PropertySchema(
|
||||||
id: 12,
|
id: 14,
|
||||||
name: r'timestamp',
|
name: r'timestamp',
|
||||||
type: IsarType.long,
|
type: IsarType.long,
|
||||||
),
|
),
|
||||||
r'txid': PropertySchema(
|
r'txid': PropertySchema(
|
||||||
id: 13,
|
id: 15,
|
||||||
name: r'txid',
|
name: r'txid',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'type': PropertySchema(
|
r'type': PropertySchema(
|
||||||
id: 14,
|
id: 16,
|
||||||
name: r'type',
|
name: r'type',
|
||||||
type: IsarType.byte,
|
type: IsarType.byte,
|
||||||
enumMap: _TransactionV2typeEnumValueMap,
|
enumMap: _TransactionV2typeEnumValueMap,
|
||||||
),
|
),
|
||||||
r'version': PropertySchema(
|
r'version': PropertySchema(
|
||||||
id: 15,
|
id: 17,
|
||||||
name: r'version',
|
name: r'version',
|
||||||
type: IsarType.long,
|
type: IsarType.long,
|
||||||
),
|
),
|
||||||
r'walletId': PropertySchema(
|
r'walletId': PropertySchema(
|
||||||
id: 16,
|
id: 18,
|
||||||
name: r'walletId',
|
name: r'walletId',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
)
|
)
|
||||||
|
@ -182,6 +192,12 @@ int _transactionV2EstimateSize(
|
||||||
bytesCount += 3 + value.length * 3;
|
bytesCount += 3 + value.length * 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
final value = object.contractAddress;
|
||||||
|
if (value != null) {
|
||||||
|
bytesCount += 3 + value.length * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
bytesCount += 3 + object.hash.length * 3;
|
bytesCount += 3 + object.hash.length * 3;
|
||||||
bytesCount += 3 + object.inputs.length * 3;
|
bytesCount += 3 + object.inputs.length * 3;
|
||||||
{
|
{
|
||||||
|
@ -229,32 +245,34 @@ void _transactionV2Serialize(
|
||||||
Map<Type, List<int>> allOffsets,
|
Map<Type, List<int>> allOffsets,
|
||||||
) {
|
) {
|
||||||
writer.writeString(offsets[0], object.blockHash);
|
writer.writeString(offsets[0], object.blockHash);
|
||||||
writer.writeString(offsets[1], object.hash);
|
writer.writeString(offsets[1], object.contractAddress);
|
||||||
writer.writeLong(offsets[2], object.height);
|
writer.writeString(offsets[2], object.hash);
|
||||||
|
writer.writeLong(offsets[3], object.height);
|
||||||
writer.writeObjectList<InputV2>(
|
writer.writeObjectList<InputV2>(
|
||||||
offsets[3],
|
offsets[4],
|
||||||
allOffsets,
|
allOffsets,
|
||||||
InputV2Schema.serialize,
|
InputV2Schema.serialize,
|
||||||
object.inputs,
|
object.inputs,
|
||||||
);
|
);
|
||||||
writer.writeBool(offsets[4], object.isCancelled);
|
writer.writeBool(offsets[5], object.isCancelled);
|
||||||
writer.writeBool(offsets[5], object.isEpiccashTransaction);
|
writer.writeBool(offsets[6], object.isEpiccashTransaction);
|
||||||
writer.writeLong(offsets[6], object.numberOfMessages);
|
writer.writeLong(offsets[7], object.nonce);
|
||||||
writer.writeString(offsets[7], object.onChainNote);
|
writer.writeLong(offsets[8], object.numberOfMessages);
|
||||||
writer.writeString(offsets[8], object.otherData);
|
writer.writeString(offsets[9], object.onChainNote);
|
||||||
|
writer.writeString(offsets[10], object.otherData);
|
||||||
writer.writeObjectList<OutputV2>(
|
writer.writeObjectList<OutputV2>(
|
||||||
offsets[9],
|
offsets[11],
|
||||||
allOffsets,
|
allOffsets,
|
||||||
OutputV2Schema.serialize,
|
OutputV2Schema.serialize,
|
||||||
object.outputs,
|
object.outputs,
|
||||||
);
|
);
|
||||||
writer.writeString(offsets[10], object.slateId);
|
writer.writeString(offsets[12], object.slateId);
|
||||||
writer.writeByte(offsets[11], object.subType.index);
|
writer.writeByte(offsets[13], object.subType.index);
|
||||||
writer.writeLong(offsets[12], object.timestamp);
|
writer.writeLong(offsets[14], object.timestamp);
|
||||||
writer.writeString(offsets[13], object.txid);
|
writer.writeString(offsets[15], object.txid);
|
||||||
writer.writeByte(offsets[14], object.type.index);
|
writer.writeByte(offsets[16], object.type.index);
|
||||||
writer.writeLong(offsets[15], object.version);
|
writer.writeLong(offsets[17], object.version);
|
||||||
writer.writeString(offsets[16], object.walletId);
|
writer.writeString(offsets[18], object.walletId);
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionV2 _transactionV2Deserialize(
|
TransactionV2 _transactionV2Deserialize(
|
||||||
|
@ -265,32 +283,32 @@ TransactionV2 _transactionV2Deserialize(
|
||||||
) {
|
) {
|
||||||
final object = TransactionV2(
|
final object = TransactionV2(
|
||||||
blockHash: reader.readStringOrNull(offsets[0]),
|
blockHash: reader.readStringOrNull(offsets[0]),
|
||||||
hash: reader.readString(offsets[1]),
|
hash: reader.readString(offsets[2]),
|
||||||
height: reader.readLongOrNull(offsets[2]),
|
height: reader.readLongOrNull(offsets[3]),
|
||||||
inputs: reader.readObjectList<InputV2>(
|
inputs: reader.readObjectList<InputV2>(
|
||||||
offsets[3],
|
offsets[4],
|
||||||
InputV2Schema.deserialize,
|
InputV2Schema.deserialize,
|
||||||
allOffsets,
|
allOffsets,
|
||||||
InputV2(),
|
InputV2(),
|
||||||
) ??
|
) ??
|
||||||
[],
|
[],
|
||||||
otherData: reader.readStringOrNull(offsets[8]),
|
otherData: reader.readStringOrNull(offsets[10]),
|
||||||
outputs: reader.readObjectList<OutputV2>(
|
outputs: reader.readObjectList<OutputV2>(
|
||||||
offsets[9],
|
offsets[11],
|
||||||
OutputV2Schema.deserialize,
|
OutputV2Schema.deserialize,
|
||||||
allOffsets,
|
allOffsets,
|
||||||
OutputV2(),
|
OutputV2(),
|
||||||
) ??
|
) ??
|
||||||
[],
|
[],
|
||||||
subType:
|
subType:
|
||||||
_TransactionV2subTypeValueEnumMap[reader.readByteOrNull(offsets[11])] ??
|
_TransactionV2subTypeValueEnumMap[reader.readByteOrNull(offsets[13])] ??
|
||||||
TransactionSubType.none,
|
TransactionSubType.none,
|
||||||
timestamp: reader.readLong(offsets[12]),
|
timestamp: reader.readLong(offsets[14]),
|
||||||
txid: reader.readString(offsets[13]),
|
txid: reader.readString(offsets[15]),
|
||||||
type: _TransactionV2typeValueEnumMap[reader.readByteOrNull(offsets[14])] ??
|
type: _TransactionV2typeValueEnumMap[reader.readByteOrNull(offsets[16])] ??
|
||||||
TransactionType.outgoing,
|
TransactionType.outgoing,
|
||||||
version: reader.readLong(offsets[15]),
|
version: reader.readLong(offsets[17]),
|
||||||
walletId: reader.readString(offsets[16]),
|
walletId: reader.readString(offsets[18]),
|
||||||
);
|
);
|
||||||
object.id = id;
|
object.id = id;
|
||||||
return object;
|
return object;
|
||||||
|
@ -306,10 +324,12 @@ P _transactionV2DeserializeProp<P>(
|
||||||
case 0:
|
case 0:
|
||||||
return (reader.readStringOrNull(offset)) as P;
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
case 1:
|
case 1:
|
||||||
return (reader.readString(offset)) as P;
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
case 2:
|
case 2:
|
||||||
return (reader.readLongOrNull(offset)) as P;
|
return (reader.readString(offset)) as P;
|
||||||
case 3:
|
case 3:
|
||||||
|
return (reader.readLongOrNull(offset)) as P;
|
||||||
|
case 4:
|
||||||
return (reader.readObjectList<InputV2>(
|
return (reader.readObjectList<InputV2>(
|
||||||
offset,
|
offset,
|
||||||
InputV2Schema.deserialize,
|
InputV2Schema.deserialize,
|
||||||
|
@ -317,17 +337,19 @@ P _transactionV2DeserializeProp<P>(
|
||||||
InputV2(),
|
InputV2(),
|
||||||
) ??
|
) ??
|
||||||
[]) as P;
|
[]) as P;
|
||||||
case 4:
|
|
||||||
return (reader.readBool(offset)) as P;
|
|
||||||
case 5:
|
case 5:
|
||||||
return (reader.readBool(offset)) as P;
|
return (reader.readBool(offset)) as P;
|
||||||
case 6:
|
case 6:
|
||||||
return (reader.readLongOrNull(offset)) as P;
|
return (reader.readBool(offset)) as P;
|
||||||
case 7:
|
case 7:
|
||||||
return (reader.readStringOrNull(offset)) as P;
|
return (reader.readLongOrNull(offset)) as P;
|
||||||
case 8:
|
case 8:
|
||||||
return (reader.readStringOrNull(offset)) as P;
|
return (reader.readLongOrNull(offset)) as P;
|
||||||
case 9:
|
case 9:
|
||||||
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
|
case 10:
|
||||||
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
|
case 11:
|
||||||
return (reader.readObjectList<OutputV2>(
|
return (reader.readObjectList<OutputV2>(
|
||||||
offset,
|
offset,
|
||||||
OutputV2Schema.deserialize,
|
OutputV2Schema.deserialize,
|
||||||
|
@ -335,22 +357,22 @@ P _transactionV2DeserializeProp<P>(
|
||||||
OutputV2(),
|
OutputV2(),
|
||||||
) ??
|
) ??
|
||||||
[]) as P;
|
[]) as P;
|
||||||
case 10:
|
case 12:
|
||||||
return (reader.readStringOrNull(offset)) as P;
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
case 11:
|
case 13:
|
||||||
return (_TransactionV2subTypeValueEnumMap[
|
return (_TransactionV2subTypeValueEnumMap[
|
||||||
reader.readByteOrNull(offset)] ??
|
reader.readByteOrNull(offset)] ??
|
||||||
TransactionSubType.none) as P;
|
TransactionSubType.none) as P;
|
||||||
case 12:
|
|
||||||
return (reader.readLong(offset)) as P;
|
|
||||||
case 13:
|
|
||||||
return (reader.readString(offset)) as P;
|
|
||||||
case 14:
|
case 14:
|
||||||
|
return (reader.readLong(offset)) as P;
|
||||||
|
case 15:
|
||||||
|
return (reader.readString(offset)) as P;
|
||||||
|
case 16:
|
||||||
return (_TransactionV2typeValueEnumMap[reader.readByteOrNull(offset)] ??
|
return (_TransactionV2typeValueEnumMap[reader.readByteOrNull(offset)] ??
|
||||||
TransactionType.outgoing) as P;
|
TransactionType.outgoing) as P;
|
||||||
case 15:
|
case 17:
|
||||||
return (reader.readLong(offset)) as P;
|
return (reader.readLong(offset)) as P;
|
||||||
case 16:
|
case 18:
|
||||||
return (reader.readString(offset)) as P;
|
return (reader.readString(offset)) as P;
|
||||||
default:
|
default:
|
||||||
throw IsarError('Unknown property with id $propertyId');
|
throw IsarError('Unknown property with id $propertyId');
|
||||||
|
@ -963,6 +985,160 @@ extension TransactionV2QueryFilter
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressIsNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNull(
|
||||||
|
property: r'contractAddress',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressIsNotNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||||
|
property: r'contractAddress',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressEqualTo(
|
||||||
|
String? value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'contractAddress',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressGreaterThan(
|
||||||
|
String? value, {
|
||||||
|
bool include = false,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||||
|
include: include,
|
||||||
|
property: r'contractAddress',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressLessThan(
|
||||||
|
String? value, {
|
||||||
|
bool include = false,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.lessThan(
|
||||||
|
include: include,
|
||||||
|
property: r'contractAddress',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressBetween(
|
||||||
|
String? lower,
|
||||||
|
String? upper, {
|
||||||
|
bool includeLower = true,
|
||||||
|
bool includeUpper = true,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.between(
|
||||||
|
property: r'contractAddress',
|
||||||
|
lower: lower,
|
||||||
|
includeLower: includeLower,
|
||||||
|
upper: upper,
|
||||||
|
includeUpper: includeUpper,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressStartsWith(
|
||||||
|
String value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.startsWith(
|
||||||
|
property: r'contractAddress',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressEndsWith(
|
||||||
|
String value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.endsWith(
|
||||||
|
property: r'contractAddress',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressContains(String value, {bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.contains(
|
||||||
|
property: r'contractAddress',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressMatches(String pattern, {bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.matches(
|
||||||
|
property: r'contractAddress',
|
||||||
|
wildcard: pattern,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressIsEmpty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'contractAddress',
|
||||||
|
value: '',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
contractAddressIsNotEmpty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||||
|
property: r'contractAddress',
|
||||||
|
value: '',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition> hashEqualTo(
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition> hashEqualTo(
|
||||||
String value, {
|
String value, {
|
||||||
bool caseSensitive = true,
|
bool caseSensitive = true,
|
||||||
|
@ -1335,6 +1511,80 @@ extension TransactionV2QueryFilter
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
nonceIsNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNull(
|
||||||
|
property: r'nonce',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
nonceIsNotNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||||
|
property: r'nonce',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
nonceEqualTo(int? value) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'nonce',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
nonceGreaterThan(
|
||||||
|
int? value, {
|
||||||
|
bool include = false,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||||
|
include: include,
|
||||||
|
property: r'nonce',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
nonceLessThan(
|
||||||
|
int? value, {
|
||||||
|
bool include = false,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.lessThan(
|
||||||
|
include: include,
|
||||||
|
property: r'nonce',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
nonceBetween(
|
||||||
|
int? lower,
|
||||||
|
int? upper, {
|
||||||
|
bool includeLower = true,
|
||||||
|
bool includeUpper = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.between(
|
||||||
|
property: r'nonce',
|
||||||
|
lower: lower,
|
||||||
|
includeLower: includeLower,
|
||||||
|
upper: upper,
|
||||||
|
includeUpper: includeUpper,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
numberOfMessagesIsNull() {
|
numberOfMessagesIsNull() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
@ -2490,6 +2740,20 @@ extension TransactionV2QuerySortBy
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
||||||
|
sortByContractAddress() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'contractAddress', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
||||||
|
sortByContractAddressDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'contractAddress', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> sortByHash() {
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> sortByHash() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'hash', Sort.asc);
|
return query.addSortBy(r'hash', Sort.asc);
|
||||||
|
@ -2541,6 +2805,18 @@ extension TransactionV2QuerySortBy
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> sortByNonce() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'nonce', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> sortByNonceDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'nonce', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
||||||
sortByNumberOfMessages() {
|
sortByNumberOfMessages() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
@ -2683,6 +2959,20 @@ extension TransactionV2QuerySortThenBy
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
||||||
|
thenByContractAddress() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'contractAddress', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
||||||
|
thenByContractAddressDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'contractAddress', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> thenByHash() {
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> thenByHash() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'hash', Sort.asc);
|
return query.addSortBy(r'hash', Sort.asc);
|
||||||
|
@ -2746,6 +3036,18 @@ extension TransactionV2QuerySortThenBy
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> thenByNonce() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'nonce', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> thenByNonceDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'nonce', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
||||||
thenByNumberOfMessages() {
|
thenByNumberOfMessages() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
@ -2882,6 +3184,14 @@ extension TransactionV2QueryWhereDistinct
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QDistinct>
|
||||||
|
distinctByContractAddress({bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addDistinctBy(r'contractAddress',
|
||||||
|
caseSensitive: caseSensitive);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QDistinct> distinctByHash(
|
QueryBuilder<TransactionV2, TransactionV2, QDistinct> distinctByHash(
|
||||||
{bool caseSensitive = true}) {
|
{bool caseSensitive = true}) {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
@ -2909,6 +3219,12 @@ extension TransactionV2QueryWhereDistinct
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QDistinct> distinctByNonce() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addDistinctBy(r'nonce');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QDistinct>
|
QueryBuilder<TransactionV2, TransactionV2, QDistinct>
|
||||||
distinctByNumberOfMessages() {
|
distinctByNumberOfMessages() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
@ -2990,6 +3306,13 @@ extension TransactionV2QueryProperty
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, String?, QQueryOperations>
|
||||||
|
contractAddressProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addPropertyName(r'contractAddress');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, String, QQueryOperations> hashProperty() {
|
QueryBuilder<TransactionV2, String, QQueryOperations> hashProperty() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addPropertyName(r'hash');
|
return query.addPropertyName(r'hash');
|
||||||
|
@ -3022,6 +3345,12 @@ extension TransactionV2QueryProperty
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, int?, QQueryOperations> nonceProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addPropertyName(r'nonce');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, int?, QQueryOperations>
|
QueryBuilder<TransactionV2, int?, QQueryOperations>
|
||||||
numberOfMessagesProperty() {
|
numberOfMessagesProperty() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
|
|
@ -180,10 +180,7 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
|
||||||
|
|
||||||
tokenEntities.addAll(contracts.map((e) => AddTokenListElementData(e)));
|
tokenEntities.addAll(contracts.map((e) => AddTokenListElementData(e)));
|
||||||
|
|
||||||
final walletContracts =
|
final walletContracts = ref.read(pWalletTokenAddresses(widget.walletId));
|
||||||
(ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet)
|
|
||||||
.info
|
|
||||||
.tokenContractAddresses;
|
|
||||||
|
|
||||||
final shouldMarkAsSelectedContracts = [
|
final shouldMarkAsSelectedContracts = [
|
||||||
...walletContracts,
|
...walletContracts,
|
||||||
|
|
|
@ -20,7 +20,6 @@ import 'package:stackwallet/models/isar/models/transaction_note.dart';
|
||||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||||
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
|
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
|
||||||
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
|
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart';
|
import 'package:stackwallet/pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
|
||||||
|
@ -36,6 +35,7 @@ import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
||||||
|
@ -202,7 +202,7 @@ class _ConfirmTransactionViewState
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.isTokenTx) {
|
if (widget.isTokenTx) {
|
||||||
unawaited(ref.read(tokenServiceProvider)!.refresh());
|
unawaited(ref.read(pCurrentTokenWallet)!.refresh());
|
||||||
} else {
|
} else {
|
||||||
unawaited(wallet.refresh());
|
unawaited(wallet.refresh());
|
||||||
}
|
}
|
||||||
|
@ -345,7 +345,7 @@ class _ConfirmTransactionViewState
|
||||||
final String unit;
|
final String unit;
|
||||||
if (widget.isTokenTx) {
|
if (widget.isTokenTx) {
|
||||||
unit = ref.watch(
|
unit = ref.watch(
|
||||||
tokenServiceProvider.select((value) => value!.tokenContract.symbol));
|
pCurrentTokenWallet.select((value) => value!.tokenContract.symbol));
|
||||||
} else {
|
} else {
|
||||||
unit = coin.ticker;
|
unit = coin.ticker;
|
||||||
}
|
}
|
||||||
|
@ -518,7 +518,7 @@ class _ConfirmTransactionViewState
|
||||||
ref.watch(pAmountFormatter(coin)).format(
|
ref.watch(pAmountFormatter(coin)).format(
|
||||||
amountWithoutChange,
|
amountWithoutChange,
|
||||||
ethContract: ref
|
ethContract: ref
|
||||||
.watch(tokenServiceProvider)
|
.watch(pCurrentTokenWallet)
|
||||||
?.tokenContract,
|
?.tokenContract,
|
||||||
),
|
),
|
||||||
style: STextStyles.itemSubtitle12(context),
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
@ -708,7 +708,7 @@ class _ConfirmTransactionViewState
|
||||||
priceAnd24hChangeNotifierProvider)
|
priceAnd24hChangeNotifierProvider)
|
||||||
.getTokenPrice(
|
.getTokenPrice(
|
||||||
ref
|
ref
|
||||||
.read(tokenServiceProvider)!
|
.read(pCurrentTokenWallet)!
|
||||||
.tokenContract
|
.tokenContract
|
||||||
.address,
|
.address,
|
||||||
)
|
)
|
||||||
|
@ -737,7 +737,7 @@ class _ConfirmTransactionViewState
|
||||||
ref.watch(pAmountFormatter(coin)).format(
|
ref.watch(pAmountFormatter(coin)).format(
|
||||||
amountWithoutChange,
|
amountWithoutChange,
|
||||||
ethContract: ref
|
ethContract: ref
|
||||||
.read(tokenServiceProvider)
|
.read(pCurrentTokenWallet)
|
||||||
?.tokenContract),
|
?.tokenContract),
|
||||||
style: STextStyles
|
style: STextStyles
|
||||||
.desktopTextExtraExtraSmall(
|
.desktopTextExtraExtraSmall(
|
||||||
|
|
|
@ -12,7 +12,6 @@ import 'package:cw_core/monero_transaction_priority.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
|
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
|
||||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||||
|
@ -24,6 +23,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
||||||
import 'package:stackwallet/widgets/animated_text.dart';
|
import 'package:stackwallet/widgets/animated_text.dart';
|
||||||
|
@ -90,18 +90,28 @@ class _TransactionFeeSelectionSheetState
|
||||||
final fee = await wallet.estimateFeeFor(
|
final fee = await wallet.estimateFeeFor(
|
||||||
amount, MoneroTransactionPriority.fast.raw!);
|
amount, MoneroTransactionPriority.fast.raw!);
|
||||||
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
final Amount fee;
|
||||||
"Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
case FiroType.spark:
|
||||||
await (wallet as FiroWallet).estimateFeeForLelantus(amount);
|
fee =
|
||||||
|
await (wallet as FiroWallet).estimateFeeForSpark(amount);
|
||||||
|
case FiroType.lelantus:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
case FiroType.public:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||||
} else {
|
} else {
|
||||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||||
await wallet.estimateFeeFor(amount, feeRate);
|
await wallet.estimateFeeFor(amount, feeRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||||
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,18 +125,27 @@ class _TransactionFeeSelectionSheetState
|
||||||
final fee = await wallet.estimateFeeFor(
|
final fee = await wallet.estimateFeeFor(
|
||||||
amount, MoneroTransactionPriority.regular.raw!);
|
amount, MoneroTransactionPriority.regular.raw!);
|
||||||
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
final Amount fee;
|
||||||
"Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
case FiroType.spark:
|
||||||
await (wallet as FiroWallet).estimateFeeForLelantus(amount);
|
fee =
|
||||||
|
await (wallet as FiroWallet).estimateFeeForSpark(amount);
|
||||||
|
case FiroType.lelantus:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
case FiroType.public:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||||
} else {
|
} else {
|
||||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||||
await wallet.estimateFeeFor(amount, feeRate);
|
await wallet.estimateFeeFor(amount, feeRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||||
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,18 +159,27 @@ class _TransactionFeeSelectionSheetState
|
||||||
final fee = await wallet.estimateFeeFor(
|
final fee = await wallet.estimateFeeFor(
|
||||||
amount, MoneroTransactionPriority.slow.raw!);
|
amount, MoneroTransactionPriority.slow.raw!);
|
||||||
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
final Amount fee;
|
||||||
"Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
case FiroType.spark:
|
||||||
await (wallet as FiroWallet).estimateFeeForLelantus(amount);
|
fee =
|
||||||
|
await (wallet as FiroWallet).estimateFeeForSpark(amount);
|
||||||
|
case FiroType.lelantus:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
case FiroType.public:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||||
} else {
|
} else {
|
||||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||||
await wallet.estimateFeeFor(amount, feeRate);
|
await wallet.estimateFeeFor(amount, feeRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||||
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,7 +267,7 @@ class _TransactionFeeSelectionSheetState
|
||||||
),
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: widget.isToken
|
future: widget.isToken
|
||||||
? ref.read(tokenServiceProvider)!.fees
|
? ref.read(pCurrentTokenWallet)!.fees
|
||||||
: wallet.fees,
|
: wallet.fees,
|
||||||
builder: (context, AsyncSnapshot<FeeObject> snapshot) {
|
builder: (context, AsyncSnapshot<FeeObject> snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done &&
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
|
|
|
@ -21,7 +21,6 @@ import 'package:stackwallet/pages/address_book_views/address_book_view.dart';
|
||||||
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
||||||
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
|
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
|
||||||
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
|
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
|
||||||
import 'package:stackwallet/providers/ui/preview_tx_button_state_provider.dart';
|
import 'package:stackwallet/providers/ui/preview_tx_button_state_provider.dart';
|
||||||
|
@ -41,6 +40,8 @@ import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/token_balance_provider.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||||
import 'package:stackwallet/widgets/animated_text.dart';
|
import 'package:stackwallet/widgets/animated_text.dart';
|
||||||
|
@ -353,7 +354,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> calculateFees() async {
|
Future<String> calculateFees() async {
|
||||||
final wallet = ref.read(tokenServiceProvider)!;
|
final wallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final feeObject = await wallet.fees;
|
final feeObject = await wallet.fees;
|
||||||
|
|
||||||
late final int feeRate;
|
late final int feeRate;
|
||||||
|
@ -372,7 +373,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
feeRate = -1;
|
feeRate = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Amount fee = wallet.estimateFeeFor(feeRate);
|
final Amount fee = await wallet.estimateFeeFor(Amount.zero, feeRate);
|
||||||
cachedFees = ref.read(pAmountFormatter(coin)).format(
|
cachedFees = ref.read(pAmountFormatter(coin)).format(
|
||||||
fee,
|
fee,
|
||||||
withUnitName: true,
|
withUnitName: true,
|
||||||
|
@ -389,7 +390,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
const Duration(milliseconds: 100),
|
const Duration(milliseconds: 100),
|
||||||
);
|
);
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
|
|
||||||
final Amount amount = _amountToSend!;
|
final Amount amount = _amountToSend!;
|
||||||
|
|
||||||
|
@ -711,8 +712,11 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
.watch(pAmountFormatter(coin))
|
.watch(pAmountFormatter(coin))
|
||||||
.format(
|
.format(
|
||||||
ref
|
ref
|
||||||
.read(tokenServiceProvider)!
|
.read(pTokenBalance((
|
||||||
.balance
|
walletId: widget.walletId,
|
||||||
|
contractAddress:
|
||||||
|
tokenContract.address,
|
||||||
|
)))
|
||||||
.spendable,
|
.spendable,
|
||||||
ethContract: tokenContract,
|
ethContract: tokenContract,
|
||||||
withUnitName: false,
|
withUnitName: false,
|
||||||
|
@ -729,18 +733,16 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
ref
|
ref
|
||||||
.watch(pAmountFormatter(coin))
|
.watch(pAmountFormatter(coin))
|
||||||
.format(
|
.format(
|
||||||
ref.watch(
|
ref
|
||||||
tokenServiceProvider.select(
|
.watch(pTokenBalance((
|
||||||
(value) => value!
|
walletId:
|
||||||
.balance.spendable,
|
widget.walletId,
|
||||||
),
|
contractAddress:
|
||||||
),
|
tokenContract
|
||||||
ethContract: ref.watch(
|
.address,
|
||||||
tokenServiceProvider.select(
|
)))
|
||||||
(value) =>
|
.spendable,
|
||||||
value!.tokenContract,
|
ethContract: tokenContract,
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
style:
|
style:
|
||||||
STextStyles.titleBold12(context)
|
STextStyles.titleBold12(context)
|
||||||
|
@ -750,7 +752,13 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"${(ref.watch(tokenServiceProvider.select((value) => value!.balance.spendable.decimal)) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getTokenPrice(tokenContract.address).item1))).toAmount(
|
"${(ref.watch(pTokenBalance((
|
||||||
|
walletId:
|
||||||
|
widget.walletId,
|
||||||
|
contractAddress:
|
||||||
|
tokenContract
|
||||||
|
.address,
|
||||||
|
))).spendable.decimal * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getTokenPrice(tokenContract.address).item1))).toAmount(
|
||||||
fractionDigits: 2,
|
fractionDigits: 2,
|
||||||
).fiatString(
|
).fiatString(
|
||||||
locale: locale,
|
locale: locale,
|
||||||
|
|
|
@ -15,14 +15,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
|
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
|
||||||
import 'package:stackwallet/pages/token_view/sub_widgets/my_tokens_list.dart';
|
import 'package:stackwallet/pages/token_view/sub_widgets/my_tokens_list.dart';
|
||||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
|
||||||
import 'package:stackwallet/widgets/background.dart';
|
import 'package:stackwallet/widgets/background.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.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';
|
||||||
|
@ -233,11 +231,7 @@ class _MyTokensViewState extends ConsumerState<MyTokensView> {
|
||||||
child: MyTokensList(
|
child: MyTokensList(
|
||||||
walletId: widget.walletId,
|
walletId: widget.walletId,
|
||||||
searchTerm: _searchString,
|
searchTerm: _searchString,
|
||||||
tokenContracts: ref
|
tokenContracts: ref.watch(pWalletTokenAddresses(widget.walletId)),
|
||||||
.watch(pWallets.select((value) =>
|
|
||||||
value.getWallet(widget.walletId) as EthereumWallet))
|
|
||||||
.info
|
|
||||||
.tokenContractAddresses,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -8,16 +8,16 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart';
|
||||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/services/ethereum/cached_eth_token_balance.dart';
|
import 'package:stackwallet/services/ethereum/cached_eth_token_balance.dart';
|
||||||
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
|
|
||||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
|
@ -25,8 +25,11 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/show_loading.dart';
|
import 'package:stackwallet/utilities/show_loading.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/token_balance_provider.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
import 'package:stackwallet/widgets/dialogs/basic_dialog.dart';
|
import 'package:stackwallet/widgets/dialogs/basic_dialog.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
|
||||||
|
@ -56,7 +59,7 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
await ref.read(tokenServiceProvider)!.initialize();
|
await ref.read(pCurrentTokenWallet)!.init();
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
await showDialog<void>(
|
await showDialog<void>(
|
||||||
|
@ -82,14 +85,12 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onPressed() async {
|
void _onPressed() async {
|
||||||
|
final old = ref.read(tokenServiceStateProvider);
|
||||||
|
// exit previous if there is one
|
||||||
|
unawaited(old?.exit());
|
||||||
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
|
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
|
||||||
token: widget.token,
|
|
||||||
secureStore: ref.read(secureStoreProvider),
|
|
||||||
ethWallet:
|
|
||||||
ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet,
|
ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet,
|
||||||
tracker: TransactionNotificationTracker(
|
widget.token,
|
||||||
walletId: widget.walletId,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final success = await showLoading<bool>(
|
final success = await showLoading<bool>(
|
||||||
|
@ -116,11 +117,14 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
|
||||||
cachedBalance = CachedEthTokenBalance(widget.walletId, widget.token);
|
cachedBalance = CachedEthTokenBalance(widget.walletId, widget.token);
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
if (mounted) {
|
||||||
final address = ref.read(pWalletReceivingAddress(widget.walletId));
|
final address = ref.read(pWalletReceivingAddress(widget.walletId));
|
||||||
await cachedBalance.fetchAndUpdateCachedBalance(address);
|
await cachedBalance.fetchAndUpdateCachedBalance(
|
||||||
|
address, ref.read(mainDBProvider));
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -172,7 +176,14 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
Text(
|
||||||
ref.watch(pAmountFormatter(Coin.ethereum)).format(
|
ref.watch(pAmountFormatter(Coin.ethereum)).format(
|
||||||
cachedBalance.getCachedBalance().total,
|
ref
|
||||||
|
.watch(pTokenBalance(
|
||||||
|
(
|
||||||
|
walletId: widget.walletId,
|
||||||
|
contractAddress: widget.token.address
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.total,
|
||||||
ethContract: widget.token,
|
ethContract: widget.token,
|
||||||
),
|
),
|
||||||
style: isDesktop
|
style: isDesktop
|
||||||
|
|
|
@ -19,7 +19,6 @@ import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart';
|
||||||
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
|
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
|
||||||
import 'package:stackwallet/pages/receive_view/receive_view.dart';
|
import 'package:stackwallet/pages/receive_view/receive_view.dart';
|
||||||
import 'package:stackwallet/pages/send_view/token_send_view.dart';
|
import 'package:stackwallet/pages/send_view/token_send_view.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
|
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
|
||||||
import 'package:stackwallet/providers/global/locale_provider.dart';
|
import 'package:stackwallet/providers/global/locale_provider.dart';
|
||||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||||
|
@ -33,6 +32,8 @@ import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/token_balance_provider.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||||
|
@ -51,9 +52,9 @@ class TokenSummary extends ConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final token =
|
final token =
|
||||||
ref.watch(tokenServiceProvider.select((value) => value!.tokenContract));
|
ref.watch(pCurrentTokenWallet.select((value) => value!.tokenContract));
|
||||||
final balance =
|
final balance = ref.watch(
|
||||||
ref.watch(tokenServiceProvider.select((value) => value!.balance));
|
pTokenBalance((walletId: walletId, contractAddress: token.address)));
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
|
@ -157,7 +158,7 @@ class TokenSummary extends ConsumerWidget {
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
initialSyncStatus: initialSyncStatus,
|
initialSyncStatus: initialSyncStatus,
|
||||||
tokenContractAddress: ref.watch(
|
tokenContractAddress: ref.watch(
|
||||||
tokenServiceProvider.select(
|
pCurrentTokenWallet.select(
|
||||||
(value) => value!.tokenContract.address,
|
(value) => value!.tokenContract.address,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -12,25 +12,18 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
|
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart';
|
import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart';
|
||||||
import 'package:stackwallet/providers/global/trades_service_provider.dart';
|
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart';
|
||||||
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||||
import 'package:stackwallet/route_generator.dart';
|
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||||
import 'package:stackwallet/widgets/trade_card.dart';
|
|
||||||
import 'package:stackwallet/widgets/transaction_card.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class TokenTransactionsList extends ConsumerStatefulWidget {
|
class TokenTransactionsList extends ConsumerStatefulWidget {
|
||||||
const TokenTransactionsList({
|
const TokenTransactionsList({
|
||||||
|
@ -49,7 +42,10 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
||||||
late final int minConfirms;
|
late final int minConfirms;
|
||||||
|
|
||||||
bool _hasLoaded = false;
|
bool _hasLoaded = false;
|
||||||
List<Transaction> _transactions2 = [];
|
List<TransactionV2> _transactions = [];
|
||||||
|
|
||||||
|
late final StreamSubscription<List<TransactionV2>> _subscription;
|
||||||
|
late final QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> _query;
|
||||||
|
|
||||||
BorderRadius get _borderRadiusFirst {
|
BorderRadius get _borderRadiusFirst {
|
||||||
return BorderRadius.only(
|
return BorderRadius.only(
|
||||||
|
@ -73,139 +69,6 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget itemBuilder(
|
|
||||||
BuildContext context,
|
|
||||||
Transaction tx,
|
|
||||||
BorderRadius? radius,
|
|
||||||
Coin coin,
|
|
||||||
) {
|
|
||||||
final matchingTrades = ref
|
|
||||||
.read(tradesServiceProvider)
|
|
||||||
.trades
|
|
||||||
.where((e) => e.payInTxid == tx.txid || e.payOutTxid == tx.txid);
|
|
||||||
|
|
||||||
if (tx.type == TransactionType.outgoing && matchingTrades.isNotEmpty) {
|
|
||||||
final trade = matchingTrades.first;
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
|
||||||
borderRadius: radius,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
TransactionCard(
|
|
||||||
// this may mess with combined firo transactions
|
|
||||||
key: tx.isConfirmed(
|
|
||||||
ref.watch(pWalletChainHeight(widget.walletId)),
|
|
||||||
minConfirms)
|
|
||||||
? Key(tx.txid + tx.type.name + tx.address.value.toString())
|
|
||||||
: UniqueKey(), //
|
|
||||||
transaction: tx,
|
|
||||||
walletId: widget.walletId,
|
|
||||||
),
|
|
||||||
TradeCard(
|
|
||||||
// this may mess with combined firo transactions
|
|
||||||
key: Key(tx.txid +
|
|
||||||
tx.type.name +
|
|
||||||
tx.address.value.toString() +
|
|
||||||
trade.uuid), //
|
|
||||||
trade: trade,
|
|
||||||
onTap: () async {
|
|
||||||
final walletName = ref.read(pWalletName(widget.walletId));
|
|
||||||
if (Util.isDesktop) {
|
|
||||||
await showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => Navigator(
|
|
||||||
initialRoute: TradeDetailsView.routeName,
|
|
||||||
onGenerateRoute: RouteGenerator.generateRoute,
|
|
||||||
onGenerateInitialRoutes: (_, __) {
|
|
||||||
return [
|
|
||||||
FadePageRoute(
|
|
||||||
DesktopDialog(
|
|
||||||
maxHeight: null,
|
|
||||||
maxWidth: 580,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 32,
|
|
||||||
bottom: 16,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Trade details",
|
|
||||||
style: STextStyles.desktopH3(context),
|
|
||||||
),
|
|
||||||
DesktopDialogCloseButton(
|
|
||||||
onPressedOverride: Navigator.of(
|
|
||||||
context,
|
|
||||||
rootNavigator: true,
|
|
||||||
).pop,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Flexible(
|
|
||||||
child: TradeDetailsView(
|
|
||||||
tradeId: trade.tradeId,
|
|
||||||
transactionIfSentFromStack: tx,
|
|
||||||
walletName: walletName,
|
|
||||||
walletId: widget.walletId,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const RouteSettings(
|
|
||||||
name: TradeDetailsView.routeName,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unawaited(
|
|
||||||
Navigator.of(context).pushNamed(
|
|
||||||
TradeDetailsView.routeName,
|
|
||||||
arguments: Tuple4(
|
|
||||||
trade.tradeId,
|
|
||||||
tx,
|
|
||||||
widget.walletId,
|
|
||||||
walletName,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
|
||||||
borderRadius: radius,
|
|
||||||
),
|
|
||||||
child: TransactionCard(
|
|
||||||
// this may mess with combined firo transactions
|
|
||||||
key: tx.isConfirmed(
|
|
||||||
ref.watch(pWalletChainHeight(widget.walletId)), minConfirms)
|
|
||||||
? Key(tx.txid + tx.type.name + tx.address.value.toString())
|
|
||||||
: UniqueKey(),
|
|
||||||
transaction: tx,
|
|
||||||
walletId: widget.walletId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
minConfirms = ref
|
minConfirms = ref
|
||||||
|
@ -213,21 +76,44 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
||||||
.getWallet(widget.walletId)
|
.getWallet(widget.walletId)
|
||||||
.cryptoCurrency
|
.cryptoCurrency
|
||||||
.minConfirms;
|
.minConfirms;
|
||||||
|
|
||||||
|
_query = ref
|
||||||
|
.read(mainDBProvider)
|
||||||
|
.isar
|
||||||
|
.transactionV2s
|
||||||
|
.where()
|
||||||
|
.walletIdEqualTo(widget.walletId)
|
||||||
|
.filter()
|
||||||
|
.subTypeEqualTo(TransactionSubType.ethToken)
|
||||||
|
.sortByTimestampDesc();
|
||||||
|
|
||||||
|
_subscription = _query.watch().listen((event) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
setState(() {
|
||||||
|
_transactions = event;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_subscription.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final wallet =
|
final wallet =
|
||||||
ref.watch(pWallets.select((value) => value.getWallet(widget.walletId)));
|
ref.watch(pWallets.select((value) => value.getWallet(widget.walletId)));
|
||||||
|
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: ref
|
future: _query.findAll(),
|
||||||
.watch(tokenServiceProvider.select((value) => value!.transactions)),
|
builder: (fbContext, AsyncSnapshot<List<TransactionV2>> snapshot) {
|
||||||
builder: (fbContext, AsyncSnapshot<List<Transaction>> snapshot) {
|
|
||||||
if (snapshot.connectionState == ConnectionState.done &&
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
snapshot.hasData) {
|
snapshot.hasData) {
|
||||||
_transactions2 = snapshot.data!;
|
_transactions = snapshot.data!;
|
||||||
_hasLoaded = true;
|
_hasLoaded = true;
|
||||||
}
|
}
|
||||||
if (!_hasLoaded) {
|
if (!_hasLoaded) {
|
||||||
|
@ -246,35 +132,34 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (_transactions2.isEmpty) {
|
if (_transactions.isEmpty) {
|
||||||
return const NoTransActionsFound();
|
return const NoTransActionsFound();
|
||||||
} else {
|
} else {
|
||||||
_transactions2.sort((a, b) => b.timestamp - a.timestamp);
|
_transactions.sort((a, b) => b.timestamp - a.timestamp);
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
if (!ref.read(tokenServiceProvider)!.isRefreshing) {
|
if (!ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked) {
|
||||||
unawaited(ref.read(tokenServiceProvider)!.refresh());
|
unawaited(ref.read(pCurrentTokenWallet)!.refresh());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Util.isDesktop
|
child: Util.isDesktop
|
||||||
? ListView.separated(
|
? ListView.separated(
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
BorderRadius? radius;
|
BorderRadius? radius;
|
||||||
if (_transactions2.length == 1) {
|
if (_transactions.length == 1) {
|
||||||
radius = BorderRadius.circular(
|
radius = BorderRadius.circular(
|
||||||
Constants.size.circularBorderRadius,
|
Constants.size.circularBorderRadius,
|
||||||
);
|
);
|
||||||
} else if (index == _transactions2.length - 1) {
|
} else if (index == _transactions.length - 1) {
|
||||||
radius = _borderRadiusLast;
|
radius = _borderRadiusLast;
|
||||||
} else if (index == 0) {
|
} else if (index == 0) {
|
||||||
radius = _borderRadiusFirst;
|
radius = _borderRadiusFirst;
|
||||||
}
|
}
|
||||||
final tx = _transactions2[index];
|
final tx = _transactions[index];
|
||||||
return itemBuilder(
|
return TxListItem(
|
||||||
context,
|
tx: tx,
|
||||||
tx,
|
coin: wallet.info.coin,
|
||||||
radius,
|
radius: radius,
|
||||||
wallet.info.coin,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (context, index) {
|
separatorBuilder: (context, index) {
|
||||||
|
@ -286,27 +171,26 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
||||||
.background,
|
.background,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: _transactions2.length,
|
itemCount: _transactions.length,
|
||||||
)
|
)
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
itemCount: _transactions2.length,
|
itemCount: _transactions.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
BorderRadius? radius;
|
BorderRadius? radius;
|
||||||
if (_transactions2.length == 1) {
|
if (_transactions.length == 1) {
|
||||||
radius = BorderRadius.circular(
|
radius = BorderRadius.circular(
|
||||||
Constants.size.circularBorderRadius,
|
Constants.size.circularBorderRadius,
|
||||||
);
|
);
|
||||||
} else if (index == _transactions2.length - 1) {
|
} else if (index == _transactions.length - 1) {
|
||||||
radius = _borderRadiusLast;
|
radius = _borderRadiusLast;
|
||||||
} else if (index == 0) {
|
} else if (index == 0) {
|
||||||
radius = _borderRadiusFirst;
|
radius = _borderRadiusFirst;
|
||||||
}
|
}
|
||||||
final tx = _transactions2[index];
|
final tx = _transactions[index];
|
||||||
return itemBuilder(
|
return TxListItem(
|
||||||
context,
|
tx: tx,
|
||||||
tx,
|
coin: wallet.info.coin,
|
||||||
radius,
|
radius: radius,
|
||||||
wallet.info.coin,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -15,23 +15,19 @@ import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/pages/token_view/sub_widgets/token_summary.dart';
|
import 'package:stackwallet/pages/token_view/sub_widgets/token_summary.dart';
|
||||||
import 'package:stackwallet/pages/token_view/sub_widgets/token_transaction_list_widget.dart';
|
import 'package:stackwallet/pages/token_view/sub_widgets/token_transaction_list_widget.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_contract_details_view.dart';
|
import 'package:stackwallet/pages/token_view/token_contract_details_view.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart';
|
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart';
|
||||||
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
|
|
||||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/widgets/background.dart';
|
import 'package:stackwallet/widgets/background.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/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
final tokenServiceStateProvider = StateProvider<EthTokenWallet?>((ref) => null);
|
|
||||||
final tokenServiceProvider = ChangeNotifierProvider<EthTokenWallet?>(
|
|
||||||
(ref) => ref.watch(tokenServiceStateProvider));
|
|
||||||
|
|
||||||
/// [eventBus] should only be set during testing
|
/// [eventBus] should only be set during testing
|
||||||
class TokenView extends ConsumerStatefulWidget {
|
class TokenView extends ConsumerStatefulWidget {
|
||||||
const TokenView({
|
const TokenView({
|
||||||
|
@ -56,7 +52,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
initialSyncStatus = ref.read(tokenServiceProvider)!.isRefreshing
|
initialSyncStatus = ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked
|
||||||
? WalletSyncStatus.syncing
|
? WalletSyncStatus.syncing
|
||||||
: WalletSyncStatus.synced;
|
: WalletSyncStatus.synced;
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -105,7 +101,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
||||||
children: [
|
children: [
|
||||||
EthTokenIcon(
|
EthTokenIcon(
|
||||||
contractAddress: ref.watch(
|
contractAddress: ref.watch(
|
||||||
tokenServiceProvider.select(
|
pCurrentTokenWallet.select(
|
||||||
(value) => value!.tokenContract.address,
|
(value) => value!.tokenContract.address,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -116,7 +112,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
ref.watch(tokenServiceProvider
|
ref.watch(pCurrentTokenWallet
|
||||||
.select((value) => value!.tokenContract.name)),
|
.select((value) => value!.tokenContract.name)),
|
||||||
style: STextStyles.navBarTitle(context),
|
style: STextStyles.navBarTitle(context),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
@ -145,7 +141,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
TokenContractDetailsView.routeName,
|
TokenContractDetailsView.routeName,
|
||||||
arguments: Tuple2(
|
arguments: Tuple2(
|
||||||
ref.watch(tokenServiceProvider
|
ref.watch(pCurrentTokenWallet
|
||||||
.select((value) => value!.tokenContract.address)),
|
.select((value) => value!.tokenContract.address)),
|
||||||
widget.walletId,
|
widget.walletId,
|
||||||
),
|
),
|
||||||
|
@ -190,7 +186,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
||||||
text: "See all",
|
text: "See all",
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
AllTransactionsView.routeName,
|
AllTransactionsV2View.routeName,
|
||||||
arguments: (
|
arguments: (
|
||||||
walletId: widget.walletId,
|
walletId: widget.walletId,
|
||||||
isTokens: true,
|
isTokens: true,
|
||||||
|
|
|
@ -13,13 +13,13 @@ import 'dart:async';
|
||||||
import 'package:event_bus/event_bus.dart';
|
import 'package:event_bus/event_bus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/widgets/animated_widgets/rotating_arrows.dart';
|
import 'package:stackwallet/widgets/animated_widgets/rotating_arrows.dart';
|
||||||
|
|
||||||
/// [eventBus] should only be set during testing
|
/// [eventBus] should only be set during testing
|
||||||
|
@ -140,8 +140,8 @@ class _RefreshButtonState extends ConsumerState<WalletRefreshButton> {
|
||||||
wallet.refresh().then((_) => _spinController.stop?.call());
|
wallet.refresh().then((_) => _spinController.stop?.call());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!ref.read(tokenServiceProvider)!.isRefreshing) {
|
if (!ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked) {
|
||||||
ref.read(tokenServiceProvider)!.refresh();
|
ref.read(pCurrentTokenWallet)!.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,7 +19,6 @@ import 'package:stackwallet/models/isar/models/contact_entry.dart';
|
||||||
import 'package:stackwallet/models/isar/models/transaction_note.dart';
|
import 'package:stackwallet/models/isar/models/transaction_note.dart';
|
||||||
import 'package:stackwallet/models/transaction_filter.dart';
|
import 'package:stackwallet/models/transaction_filter.dart';
|
||||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart';
|
import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
|
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_search_filter_view.dart';
|
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_search_filter_view.dart';
|
||||||
|
@ -61,13 +60,11 @@ class AllTransactionsView extends ConsumerStatefulWidget {
|
||||||
const AllTransactionsView({
|
const AllTransactionsView({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.walletId,
|
required this.walletId,
|
||||||
this.isTokens = false,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
static const String routeName = "/allTransactions";
|
static const String routeName = "/allTransactions";
|
||||||
|
|
||||||
final String walletId;
|
final String walletId;
|
||||||
final bool isTokens;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<AllTransactionsView> createState() =>
|
ConsumerState<AllTransactionsView> createState() =>
|
||||||
|
@ -487,24 +484,8 @@ class _TransactionDetailsViewState extends ConsumerState<AllTransactionsView> {
|
||||||
final criteria =
|
final criteria =
|
||||||
ref.watch(transactionFilterProvider.state).state;
|
ref.watch(transactionFilterProvider.state).state;
|
||||||
|
|
||||||
//todo: check if print needed
|
|
||||||
// debugPrint("Consumer build called");
|
|
||||||
|
|
||||||
final WhereClause ww;
|
|
||||||
|
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: widget.isTokens
|
future: ref.watch(mainDBProvider).isar.transactions.buildQuery<
|
||||||
? ref
|
|
||||||
.watch(mainDBProvider)
|
|
||||||
.getTransactions(walletId)
|
|
||||||
.filter()
|
|
||||||
.otherDataEqualTo(ref
|
|
||||||
.watch(tokenServiceProvider)!
|
|
||||||
.tokenContract
|
|
||||||
.address)
|
|
||||||
.sortByTimestampDesc()
|
|
||||||
.findAll()
|
|
||||||
: ref.watch(mainDBProvider).isar.transactions.buildQuery<
|
|
||||||
Transaction>(
|
Transaction>(
|
||||||
whereClauses: [
|
whereClauses: [
|
||||||
IndexWhereClause.equalTo(
|
IndexWhereClause.equalTo(
|
||||||
|
|
|
@ -25,7 +25,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet.dart';
|
import 'package:stackwallet/wallets/wallet/wallet.dart';
|
||||||
import 'package:stackwallet/widgets/background.dart';
|
import 'package:stackwallet/widgets/background.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
|
@ -119,9 +119,8 @@ class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
|
||||||
if (widget.coin == Coin.ethereum) {
|
if (widget.coin == Coin.ethereum) {
|
||||||
for (final data in walletsData) {
|
for (final data in walletsData) {
|
||||||
final List<EthContract> contracts = [];
|
final List<EthContract> contracts = [];
|
||||||
final wallet = ref.read(pWallets).getWallet(data.walletId);
|
|
||||||
final contractAddresses =
|
final contractAddresses =
|
||||||
(wallet as EthereumWallet).info.tokenContractAddresses;
|
ref.read(pWalletTokenAddresses(data.walletId));
|
||||||
|
|
||||||
// fetch each contract
|
// fetch each contract
|
||||||
for (final contractAddress in contractAddresses) {
|
for (final contractAddress in contractAddresses) {
|
||||||
|
|
|
@ -15,7 +15,6 @@ import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
||||||
import 'package:stackwallet/pages/token_view/sub_widgets/token_summary.dart';
|
import 'package:stackwallet/pages/token_view/sub_widgets/token_summary.dart';
|
||||||
import 'package:stackwallet/pages/token_view/sub_widgets/token_transaction_list_widget.dart';
|
import 'package:stackwallet/pages/token_view/sub_widgets/token_transaction_list_widget.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart';
|
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart';
|
||||||
|
@ -25,6 +24,7 @@ import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||||
|
@ -57,7 +57,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
initialSyncStatus = ref.read(tokenServiceProvider)!.isRefreshing
|
initialSyncStatus = ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked
|
||||||
? WalletSyncStatus.syncing
|
? WalletSyncStatus.syncing
|
||||||
: WalletSyncStatus.synced;
|
: WalletSyncStatus.synced;
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -114,7 +114,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
||||||
children: [
|
children: [
|
||||||
EthTokenIcon(
|
EthTokenIcon(
|
||||||
contractAddress: ref.watch(
|
contractAddress: ref.watch(
|
||||||
tokenServiceProvider.select(
|
pCurrentTokenWallet.select(
|
||||||
(value) => value!.tokenContract.address,
|
(value) => value!.tokenContract.address,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -125,7 +125,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
ref.watch(
|
ref.watch(
|
||||||
tokenServiceProvider.select(
|
pCurrentTokenWallet.select(
|
||||||
(value) => value!.tokenContract.name,
|
(value) => value!.tokenContract.name,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -153,7 +153,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
||||||
children: [
|
children: [
|
||||||
EthTokenIcon(
|
EthTokenIcon(
|
||||||
contractAddress: ref.watch(
|
contractAddress: ref.watch(
|
||||||
tokenServiceProvider.select(
|
pCurrentTokenWallet.select(
|
||||||
(value) => value!.tokenContract.address,
|
(value) => value!.tokenContract.address,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -241,7 +241,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
||||||
child: MyWallet(
|
child: MyWallet(
|
||||||
walletId: widget.walletId,
|
walletId: widget.walletId,
|
||||||
contractAddress: ref.watch(
|
contractAddress: ref.watch(
|
||||||
tokenServiceProvider.select(
|
pCurrentTokenWallet.select(
|
||||||
(value) => value!.tokenContract.address,
|
(value) => value!.tokenContract.address,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -15,7 +15,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/models/models.dart';
|
import 'package:stackwallet/models/models.dart';
|
||||||
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||||
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
|
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
|
||||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||||
|
@ -27,7 +26,9 @@ import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
||||||
import 'package:stackwallet/widgets/animated_text.dart';
|
import 'package:stackwallet/widgets/animated_text.dart';
|
||||||
|
|
||||||
final tokenFeeSessionCacheProvider =
|
final tokenFeeSessionCacheProvider =
|
||||||
|
@ -83,21 +84,27 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
|
||||||
final fee = await wallet.estimateFeeFor(
|
final fee = await wallet.estimateFeeFor(
|
||||||
amount, MoneroTransactionPriority.fast.raw!);
|
amount, MoneroTransactionPriority.fast.raw!);
|
||||||
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
final Amount fee;
|
||||||
"Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
// TODO: [prio=high] firo fees
|
case FiroType.spark:
|
||||||
throw UnimplementedError("Firo public fees");
|
fee =
|
||||||
// ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
await (wallet as FiroWallet).estimateFeeForSpark(amount);
|
||||||
// await (manager.wallet as FiroWallet)
|
case FiroType.lelantus:
|
||||||
// .estimateFeeForPublic(amount, feeRate);
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
case FiroType.public:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||||
} else {
|
} else {
|
||||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||||
await wallet.estimateFeeFor(amount, feeRate);
|
await wallet.estimateFeeFor(amount, feeRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||||
ref.read(tokenFeeSessionCacheProvider).fast[amount] = fee;
|
ref.read(tokenFeeSessionCacheProvider).fast[amount] = fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,21 +128,27 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
|
||||||
final fee = await wallet.estimateFeeFor(
|
final fee = await wallet.estimateFeeFor(
|
||||||
amount, MoneroTransactionPriority.regular.raw!);
|
amount, MoneroTransactionPriority.regular.raw!);
|
||||||
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
final Amount fee;
|
||||||
"Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
// TODO: [prio=high] firo fees
|
case FiroType.spark:
|
||||||
throw UnimplementedError("Firo public fees");
|
fee =
|
||||||
// ref.read(feeSheetSessionCacheProvider).average[amount] =
|
await (wallet as FiroWallet).estimateFeeForSpark(amount);
|
||||||
// await (manager.wallet as FiroWallet)
|
case FiroType.lelantus:
|
||||||
// .estimateFeeForPublic(amount, feeRate);
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
case FiroType.public:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||||
} else {
|
} else {
|
||||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||||
await wallet.estimateFeeFor(amount, feeRate);
|
await wallet.estimateFeeFor(amount, feeRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||||
ref.read(tokenFeeSessionCacheProvider).average[amount] = fee;
|
ref.read(tokenFeeSessionCacheProvider).average[amount] = fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,21 +172,27 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
|
||||||
final fee = await wallet.estimateFeeFor(
|
final fee = await wallet.estimateFeeFor(
|
||||||
amount, MoneroTransactionPriority.slow.raw!);
|
amount, MoneroTransactionPriority.slow.raw!);
|
||||||
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
final Amount fee;
|
||||||
"Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
// TODO: [prio=high] firo fees
|
case FiroType.spark:
|
||||||
throw UnimplementedError("Firo public fees");
|
fee =
|
||||||
// ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
await (wallet as FiroWallet).estimateFeeForSpark(amount);
|
||||||
// await (manager.wallet as FiroWallet)
|
case FiroType.lelantus:
|
||||||
// .estimateFeeForPublic(amount, feeRate);
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
case FiroType.public:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||||
} else {
|
} else {
|
||||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||||
await wallet.estimateFeeFor(amount, feeRate);
|
await wallet.estimateFeeFor(amount, feeRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||||
ref.read(tokenFeeSessionCacheProvider).slow[amount] = fee;
|
ref.read(tokenFeeSessionCacheProvider).slow[amount] = fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||||
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
|
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/route_generator.dart';
|
import 'package:stackwallet/route_generator.dart';
|
||||||
|
@ -32,6 +31,7 @@ import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||||
|
@ -307,7 +307,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Your ${widget.contractAddress == null ? coin.ticker : ref.watch(
|
"Your ${widget.contractAddress == null ? coin.ticker : ref.watch(
|
||||||
tokenServiceProvider.select(
|
pCurrentTokenWallet.select(
|
||||||
(value) => value!.tokenContract.symbol,
|
(value) => value!.tokenContract.symbol,
|
||||||
),
|
),
|
||||||
)} SPARK address",
|
)} SPARK address",
|
||||||
|
@ -398,7 +398,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Your ${widget.contractAddress == null ? coin.ticker : ref.watch(
|
"Your ${widget.contractAddress == null ? coin.ticker : ref.watch(
|
||||||
tokenServiceProvider.select(
|
pCurrentTokenWallet.select(
|
||||||
(value) => value!.tokenContract.symbol,
|
(value) => value!.tokenContract.symbol,
|
||||||
),
|
),
|
||||||
)} address",
|
)} address",
|
||||||
|
|
|
@ -19,7 +19,6 @@ import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
|
||||||
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
||||||
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
||||||
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
|
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart';
|
||||||
|
@ -39,6 +38,8 @@ import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/token_balance_provider.dart';
|
||||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||||
|
@ -103,10 +104,15 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
late VoidCallback onCryptoAmountChanged;
|
late VoidCallback onCryptoAmountChanged;
|
||||||
|
|
||||||
Future<void> previewSend() async {
|
Future<void> previewSend() async {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
|
|
||||||
final Amount amount = _amountToSend!;
|
final Amount amount = _amountToSend!;
|
||||||
final Amount availableBalance = tokenWallet.balance.spendable;
|
final Amount availableBalance = ref
|
||||||
|
.read(pTokenBalance((
|
||||||
|
walletId: walletId,
|
||||||
|
contractAddress: tokenWallet.tokenContract.address
|
||||||
|
)))
|
||||||
|
.spendable;
|
||||||
|
|
||||||
// confirm send all
|
// confirm send all
|
||||||
if (amount == availableBalance) {
|
if (amount == availableBalance) {
|
||||||
|
@ -214,7 +220,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(32),
|
padding: const EdgeInsets.all(32),
|
||||||
child: BuildingTransactionDialog(
|
child: BuildingTransactionDialog(
|
||||||
coin: tokenWallet.coin,
|
coin: tokenWallet.cryptoCurrency.coin,
|
||||||
onCancel: () {
|
onCancel: () {
|
||||||
wasCancelled = true;
|
wasCancelled = true;
|
||||||
|
|
||||||
|
@ -389,11 +395,11 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
_amountToSend = cryptoAmount.contains(",")
|
_amountToSend = cryptoAmount.contains(",")
|
||||||
? Decimal.parse(cryptoAmount.replaceFirst(",", ".")).toAmount(
|
? Decimal.parse(cryptoAmount.replaceFirst(",", ".")).toAmount(
|
||||||
fractionDigits:
|
fractionDigits:
|
||||||
ref.read(tokenServiceProvider)!.tokenContract.decimals,
|
ref.read(pCurrentTokenWallet)!.tokenContract.decimals,
|
||||||
)
|
)
|
||||||
: Decimal.parse(cryptoAmount).toAmount(
|
: Decimal.parse(cryptoAmount).toAmount(
|
||||||
fractionDigits:
|
fractionDigits:
|
||||||
ref.read(tokenServiceProvider)!.tokenContract.decimals,
|
ref.read(pCurrentTokenWallet)!.tokenContract.decimals,
|
||||||
);
|
);
|
||||||
if (_cachedAmountToSend != null &&
|
if (_cachedAmountToSend != null &&
|
||||||
_cachedAmountToSend == _amountToSend) {
|
_cachedAmountToSend == _amountToSend) {
|
||||||
|
@ -406,7 +412,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
final price = ref
|
final price = ref
|
||||||
.read(priceAnd24hChangeNotifierProvider)
|
.read(priceAnd24hChangeNotifierProvider)
|
||||||
.getTokenPrice(
|
.getTokenPrice(
|
||||||
ref.read(tokenServiceProvider)!.tokenContract.address,
|
ref.read(pCurrentTokenWallet)!.tokenContract.address,
|
||||||
)
|
)
|
||||||
.item1;
|
.item1;
|
||||||
|
|
||||||
|
@ -485,7 +491,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
if (results["amount"] != null) {
|
if (results["amount"] != null) {
|
||||||
final amount = Decimal.parse(results["amount"]!).toAmount(
|
final amount = Decimal.parse(results["amount"]!).toAmount(
|
||||||
fractionDigits:
|
fractionDigits:
|
||||||
ref.read(tokenServiceProvider)!.tokenContract.decimals,
|
ref.read(pCurrentTokenWallet)!.tokenContract.decimals,
|
||||||
);
|
);
|
||||||
cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format(
|
cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format(
|
||||||
amount,
|
amount,
|
||||||
|
@ -543,7 +549,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
|
|
||||||
void fiatTextFieldOnChanged(String baseAmountString) {
|
void fiatTextFieldOnChanged(String baseAmountString) {
|
||||||
final int tokenDecimals =
|
final int tokenDecimals =
|
||||||
ref.read(tokenServiceProvider)!.tokenContract.decimals;
|
ref.read(pCurrentTokenWallet)!.tokenContract.decimals;
|
||||||
|
|
||||||
if (baseAmountString.isNotEmpty &&
|
if (baseAmountString.isNotEmpty &&
|
||||||
baseAmountString != "." &&
|
baseAmountString != "." &&
|
||||||
|
@ -556,7 +562,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
final Decimal _price = ref
|
final Decimal _price = ref
|
||||||
.read(priceAnd24hChangeNotifierProvider)
|
.read(priceAnd24hChangeNotifierProvider)
|
||||||
.getTokenPrice(
|
.getTokenPrice(
|
||||||
ref.read(tokenServiceProvider)!.tokenContract.address,
|
ref.read(pCurrentTokenWallet)!.tokenContract.address,
|
||||||
)
|
)
|
||||||
.item1;
|
.item1;
|
||||||
|
|
||||||
|
@ -579,7 +585,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
final amountString = ref.read(pAmountFormatter(coin)).format(
|
final amountString = ref.read(pAmountFormatter(coin)).format(
|
||||||
_amountToSend!,
|
_amountToSend!,
|
||||||
withUnitName: false,
|
withUnitName: false,
|
||||||
ethContract: ref.read(tokenServiceProvider)!.tokenContract,
|
ethContract: ref.read(pCurrentTokenWallet)!.tokenContract,
|
||||||
);
|
);
|
||||||
|
|
||||||
_cryptoAmountChangeLock = true;
|
_cryptoAmountChangeLock = true;
|
||||||
|
@ -597,12 +603,14 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
|
|
||||||
Future<void> sendAllTapped() async {
|
Future<void> sendAllTapped() async {
|
||||||
cryptoAmountController.text = ref
|
cryptoAmountController.text = ref
|
||||||
.read(tokenServiceProvider)!
|
.read(pTokenBalance((
|
||||||
.balance
|
walletId: walletId,
|
||||||
|
contractAddress: ref.read(pCurrentTokenWallet)!.tokenContract.address
|
||||||
|
)))
|
||||||
.spendable
|
.spendable
|
||||||
.decimal
|
.decimal
|
||||||
.toStringAsFixed(
|
.toStringAsFixed(
|
||||||
ref.read(tokenServiceProvider)!.tokenContract.decimals,
|
ref.read(pCurrentTokenWallet)!.tokenContract.decimals,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,7 +694,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
debugPrint("BUILD: $runtimeType");
|
debugPrint("BUILD: $runtimeType");
|
||||||
|
|
||||||
final tokenContract = ref.watch(tokenServiceProvider)!.tokenContract;
|
final tokenContract = ref.watch(pCurrentTokenWallet)!.tokenContract;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/balance.dart';
|
import 'package:stackwallet/models/balance.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
|
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
|
@ -24,6 +23,8 @@ import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
|
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/token_balance_provider.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
|
|
||||||
class DesktopWalletSummary extends ConsumerStatefulWidget {
|
class DesktopWalletSummary extends ConsumerStatefulWidget {
|
||||||
|
@ -70,8 +71,7 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
|
||||||
.watch(prefsChangeNotifierProvider.select((value) => value.currency));
|
.watch(prefsChangeNotifierProvider.select((value) => value.currency));
|
||||||
|
|
||||||
final tokenContract = widget.isToken
|
final tokenContract = widget.isToken
|
||||||
? ref
|
? ref.watch(pCurrentTokenWallet.select((value) => value!.tokenContract))
|
||||||
.watch(tokenServiceProvider.select((value) => value!.tokenContract))
|
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
final priceTuple = widget.isToken
|
final priceTuple = widget.isToken
|
||||||
|
@ -104,7 +104,8 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Balance balance = widget.isToken
|
Balance balance = widget.isToken
|
||||||
? ref.watch(tokenServiceProvider.select((value) => value!.balance))
|
? ref.watch(pTokenBalance(
|
||||||
|
(walletId: walletId, contractAddress: tokenContract!.address)))
|
||||||
: ref.watch(pWalletBalance(walletId));
|
: ref.watch(pWalletBalance(walletId));
|
||||||
|
|
||||||
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Stack Wallet.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2023 Cypher Stack
|
|
||||||
* All Rights Reserved.
|
|
||||||
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
|
||||||
* Generated by Cypher Stack on 2023-05-26
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
|
||||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
|
||||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
|
||||||
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
|
|
||||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
|
||||||
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
|
||||||
|
|
||||||
class ContractWalletId implements Equatable {
|
|
||||||
final String walletId;
|
|
||||||
final String tokenContractAddress;
|
|
||||||
|
|
||||||
ContractWalletId({
|
|
||||||
required this.walletId,
|
|
||||||
required this.tokenContractAddress,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [walletId, tokenContractAddress];
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool? get stringify => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// provide the token wallet given a contract address and eth wallet id
|
|
||||||
final tokenWalletProvider =
|
|
||||||
Provider.family<EthTokenWallet?, ContractWalletId>((ref, arg) {
|
|
||||||
final ethWallet =
|
|
||||||
ref.watch(pWallets).getWallet(arg.walletId) as EthereumWallet?;
|
|
||||||
final contract =
|
|
||||||
ref.read(mainDBProvider).getEthContractSync(arg.tokenContractAddress);
|
|
||||||
|
|
||||||
if (ethWallet == null || contract == null) {
|
|
||||||
Logging.instance.log(
|
|
||||||
"Attempted to access a token wallet with walletId=${arg.walletId} where"
|
|
||||||
" contractAddress=${arg.tokenContractAddress}",
|
|
||||||
level: LogLevel.Warning,
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final secureStore = ref.watch(secureStoreProvider);
|
|
||||||
|
|
||||||
return EthTokenWallet(
|
|
||||||
token: contract,
|
|
||||||
ethWallet: ethWallet,
|
|
||||||
secureStore: secureStore,
|
|
||||||
tracker: TransactionNotificationTracker(
|
|
||||||
walletId: arg.walletId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
|
@ -1300,18 +1300,6 @@ class RouteGenerator {
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
case AllTransactionsView.routeName:
|
case AllTransactionsView.routeName:
|
||||||
if (args is ({String walletId, bool isTokens})) {
|
|
||||||
return getRoute(
|
|
||||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
|
||||||
builder: (_) => AllTransactionsView(
|
|
||||||
walletId: args.walletId,
|
|
||||||
isTokens: args.isTokens,
|
|
||||||
),
|
|
||||||
settings: RouteSettings(
|
|
||||||
name: settings.name,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (args is String) {
|
if (args is String) {
|
||||||
return getRoute(
|
return getRoute(
|
||||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
|
|
|
@ -8,29 +8,37 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
import 'package:stackwallet/db/isar/main_db.dart';
|
||||||
import 'package:stackwallet/models/balance.dart';
|
import 'package:stackwallet/models/balance.dart';
|
||||||
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
||||||
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
|
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
|
||||||
import 'package:stackwallet/services/mixins/eth_token_cache.dart';
|
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/models/token_wallet_info.dart';
|
||||||
|
|
||||||
class CachedEthTokenBalance with EthTokenCache {
|
class CachedEthTokenBalance {
|
||||||
final String walletId;
|
final String walletId;
|
||||||
final EthContract token;
|
final EthContract token;
|
||||||
|
|
||||||
CachedEthTokenBalance(this.walletId, this.token) {
|
CachedEthTokenBalance(this.walletId, this.token);
|
||||||
initCache(walletId, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> fetchAndUpdateCachedBalance(String address) async {
|
Future<void> fetchAndUpdateCachedBalance(
|
||||||
|
String address,
|
||||||
|
MainDB mainDB,
|
||||||
|
) async {
|
||||||
final response = await EthereumAPI.getWalletTokenBalance(
|
final response = await EthereumAPI.getWalletTokenBalance(
|
||||||
address: address,
|
address: address,
|
||||||
contractAddress: token.address,
|
contractAddress: token.address,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.value != null) {
|
final info = await mainDB.isar.tokenWalletInfo
|
||||||
await updateCachedBalance(
|
.where()
|
||||||
|
.walletIdTokenAddressEqualTo(walletId, token.address)
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
if (response.value != null && info != null) {
|
||||||
|
await info.updateCachedBalance(
|
||||||
Balance(
|
Balance(
|
||||||
total: response.value!,
|
total: response.value!,
|
||||||
spendable: response.value!,
|
spendable: response.value!,
|
||||||
|
@ -43,6 +51,7 @@ class CachedEthTokenBalance with EthTokenCache {
|
||||||
fractionDigits: token.decimals,
|
fractionDigits: token.decimals,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
isar: mainDB.isar,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
|
|
|
@ -1,611 +1,152 @@
|
||||||
/*
|
// /*
|
||||||
* This file is part of Stack Wallet.
|
// * This file is part of Stack Wallet.
|
||||||
*
|
// *
|
||||||
* Copyright (c) 2023 Cypher Stack
|
// * Copyright (c) 2023 Cypher Stack
|
||||||
* All Rights Reserved.
|
// * All Rights Reserved.
|
||||||
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
// * The code is distributed under GPLv3 license, see LICENSE file for details.
|
||||||
* Generated by Cypher Stack on 2023-05-26
|
// * Generated by Cypher Stack on 2023-05-26
|
||||||
*
|
// *
|
||||||
*/
|
// */
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:ethereum_addresses/ethereum_addresses.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
import 'package:stackwallet/db/isar/main_db.dart';
|
|
||||||
import 'package:stackwallet/dto/ethereum/eth_token_tx_dto.dart';
|
|
||||||
import 'package:stackwallet/dto/ethereum/eth_token_tx_extra_dto.dart';
|
|
||||||
import 'package:stackwallet/models/balance.dart';
|
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
|
||||||
import 'package:stackwallet/models/node_model.dart';
|
|
||||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
|
||||||
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
|
|
||||||
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
|
||||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
|
||||||
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
|
||||||
import 'package:stackwallet/services/mixins/eth_token_cache.dart';
|
|
||||||
import 'package:stackwallet/services/node_service.dart';
|
|
||||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
|
||||||
import 'package:stackwallet/utilities/default_nodes.dart';
|
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|
||||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
|
||||||
import 'package:stackwallet/utilities/eth_commons.dart';
|
|
||||||
import 'package:stackwallet/utilities/extensions/extensions.dart';
|
|
||||||
import 'package:stackwallet/utilities/extensions/impl/contract_abi.dart';
|
|
||||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
|
||||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
|
||||||
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
import 'package:web3dart/web3dart.dart' as web3dart;
|
|
||||||
|
|
||||||
class EthTokenWallet extends ChangeNotifier with EthTokenCache {
|
|
||||||
final EthereumWallet ethWallet;
|
|
||||||
final TransactionNotificationTracker tracker;
|
|
||||||
final SecureStorageInterface _secureStore;
|
|
||||||
|
|
||||||
// late web3dart.EthereumAddress _contractAddress;
|
|
||||||
late web3dart.EthPrivateKey _credentials;
|
|
||||||
late web3dart.DeployedContract _deployedContract;
|
|
||||||
late web3dart.ContractFunction _sendFunction;
|
|
||||||
late web3dart.Web3Client _client;
|
|
||||||
|
|
||||||
static const _gasLimit = 200000;
|
|
||||||
|
|
||||||
EthTokenWallet({
|
|
||||||
required EthContract token,
|
|
||||||
required this.ethWallet,
|
|
||||||
required SecureStorageInterface secureStore,
|
|
||||||
required this.tracker,
|
|
||||||
}) : _secureStore = secureStore,
|
|
||||||
_tokenContract = token {
|
|
||||||
// _contractAddress = web3dart.EthereumAddress.fromHex(token.address);
|
|
||||||
initCache(ethWallet.walletId, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
EthContract get tokenContract => _tokenContract;
|
|
||||||
EthContract _tokenContract;
|
|
||||||
|
|
||||||
Balance get balance => _balance ??= getCachedBalance();
|
|
||||||
Balance? _balance;
|
|
||||||
|
|
||||||
Coin get coin => Coin.ethereum;
|
|
||||||
|
|
||||||
Future<TxData> prepareSend({
|
|
||||||
required TxData txData,
|
|
||||||
}) async {
|
|
||||||
final feeRateType = txData.feeRateType!;
|
|
||||||
int fee = 0;
|
|
||||||
final feeObject = await fees;
|
|
||||||
switch (feeRateType) {
|
|
||||||
case FeeRateType.fast:
|
|
||||||
fee = feeObject.fast;
|
|
||||||
break;
|
|
||||||
case FeeRateType.average:
|
|
||||||
fee = feeObject.medium;
|
|
||||||
break;
|
|
||||||
case FeeRateType.slow:
|
|
||||||
fee = feeObject.slow;
|
|
||||||
break;
|
|
||||||
case FeeRateType.custom:
|
|
||||||
throw UnimplementedError("custom eth token fees");
|
|
||||||
}
|
|
||||||
|
|
||||||
final feeEstimate = estimateFeeFor(fee);
|
|
||||||
|
|
||||||
final client = await getEthClient();
|
|
||||||
|
|
||||||
final myAddress = await currentReceivingAddress;
|
|
||||||
final myWeb3Address = web3dart.EthereumAddress.fromHex(myAddress);
|
|
||||||
|
|
||||||
final nonce = txData.nonce ??
|
|
||||||
await client.getTransactionCount(myWeb3Address,
|
|
||||||
atBlock: const web3dart.BlockNum.pending());
|
|
||||||
|
|
||||||
final amount = txData.recipients!.first.amount;
|
|
||||||
final address = txData.recipients!.first.address;
|
|
||||||
|
|
||||||
final tx = web3dart.Transaction.callContract(
|
|
||||||
contract: _deployedContract,
|
|
||||||
function: _sendFunction,
|
|
||||||
parameters: [web3dart.EthereumAddress.fromHex(address), amount.raw],
|
|
||||||
maxGas: _gasLimit,
|
|
||||||
gasPrice: web3dart.EtherAmount.fromUnitAndValue(
|
|
||||||
web3dart.EtherUnit.wei,
|
|
||||||
fee,
|
|
||||||
),
|
|
||||||
nonce: nonce,
|
|
||||||
);
|
|
||||||
|
|
||||||
return txData.copyWith(
|
|
||||||
fee: feeEstimate,
|
|
||||||
feeInWei: BigInt.from(fee),
|
|
||||||
web3dartTransaction: tx,
|
|
||||||
chainId: await client.getChainId(),
|
|
||||||
nonce: tx.nonce,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> confirmSend({required Map<String, dynamic> txData}) async {
|
|
||||||
try {
|
|
||||||
final txid = await _client.sendTransaction(
|
|
||||||
_credentials,
|
|
||||||
txData["ethTx"] as web3dart.Transaction,
|
|
||||||
chainId: txData["chainId"] as int,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
txData["txid"] = txid;
|
|
||||||
await updateSentCachedTxData(txData);
|
|
||||||
} catch (e, s) {
|
|
||||||
// do not rethrow as that would get handled as a send failure further up
|
|
||||||
// also this is not critical code and transaction should show up on \
|
|
||||||
// refresh regardless
|
|
||||||
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
return txid;
|
|
||||||
} catch (e) {
|
|
||||||
// rethrow to pass error in alert
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async {
|
|
||||||
final txid = txData["txid"] as String;
|
|
||||||
final addressString = checksumEthereumAddress(txData["address"] as String);
|
|
||||||
final response = await EthereumAPI.getEthTransactionByHash(txid);
|
|
||||||
|
|
||||||
final transaction = Transaction(
|
|
||||||
walletId: ethWallet.walletId,
|
|
||||||
txid: txid,
|
|
||||||
timestamp: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
|
||||||
type: TransactionType.outgoing,
|
|
||||||
subType: TransactionSubType.ethToken,
|
|
||||||
// precision may be lost here hence the following amountString
|
|
||||||
amount: (txData["recipientAmt"] as Amount).raw.toInt(),
|
|
||||||
amountString: (txData["recipientAmt"] as Amount).toJsonString(),
|
|
||||||
fee: (txData["fee"] as Amount).raw.toInt(),
|
|
||||||
height: null,
|
|
||||||
isCancelled: false,
|
|
||||||
isLelantus: false,
|
|
||||||
otherData: tokenContract.address,
|
|
||||||
slateId: null,
|
|
||||||
nonce: (txData["nonce"] as int?) ??
|
|
||||||
response.value?.nonce.toBigIntFromHex.toInt(),
|
|
||||||
inputs: [],
|
|
||||||
outputs: [],
|
|
||||||
numberOfMessages: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
Address? address = await ethWallet.mainDB.getAddress(
|
|
||||||
ethWallet.walletId,
|
|
||||||
addressString,
|
|
||||||
);
|
|
||||||
|
|
||||||
address ??= Address(
|
|
||||||
walletId: ethWallet.walletId,
|
|
||||||
value: addressString,
|
|
||||||
publicKey: [],
|
|
||||||
derivationIndex: -1,
|
|
||||||
derivationPath: null,
|
|
||||||
type: AddressType.ethereum,
|
|
||||||
subType: AddressSubType.nonWallet,
|
|
||||||
);
|
|
||||||
|
|
||||||
await ethWallet.mainDB.addNewTransactionData(
|
|
||||||
[
|
|
||||||
Tuple2(transaction, address),
|
|
||||||
],
|
|
||||||
ethWallet.walletId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> get currentReceivingAddress async {
|
|
||||||
final address = await _currentReceivingAddress;
|
|
||||||
return checksumEthereumAddress(
|
|
||||||
address?.value ?? _credentials.address.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Address?> get _currentReceivingAddress => ethWallet.mainDB
|
|
||||||
.getAddresses(ethWallet.walletId)
|
|
||||||
.filter()
|
|
||||||
.typeEqualTo(AddressType.ethereum)
|
|
||||||
.subTypeEqualTo(AddressSubType.receiving)
|
|
||||||
.sortByDerivationIndexDesc()
|
|
||||||
.findFirst();
|
|
||||||
|
|
||||||
Amount estimateFeeFor(int feeRate) {
|
|
||||||
return estimateFee(feeRate, _gasLimit, coin.decimals);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<FeeObject> get fees => EthereumAPI.getFees();
|
|
||||||
|
|
||||||
Future<EthContract> _updateTokenABI({
|
|
||||||
required EthContract forContract,
|
|
||||||
required String usingContractAddress,
|
|
||||||
}) async {
|
|
||||||
final abiResponse = await EthereumAPI.getTokenAbi(
|
|
||||||
name: forContract.name,
|
|
||||||
contractAddress: usingContractAddress,
|
|
||||||
);
|
|
||||||
// Fetch token ABI so we can call token functions
|
|
||||||
if (abiResponse.value != null) {
|
|
||||||
final updatedToken = forContract.copyWith(abi: abiResponse.value!);
|
|
||||||
// Store updated contract
|
|
||||||
final id = await MainDB.instance.putEthContract(updatedToken);
|
|
||||||
return updatedToken..id = id;
|
|
||||||
} else {
|
|
||||||
throw abiResponse.exception!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> initialize() async {
|
|
||||||
final contractAddress =
|
|
||||||
web3dart.EthereumAddress.fromHex(tokenContract.address);
|
|
||||||
|
|
||||||
if (tokenContract.abi == null) {
|
|
||||||
_tokenContract = await _updateTokenABI(
|
|
||||||
forContract: tokenContract,
|
|
||||||
usingContractAddress: contractAddress.hex,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String? mnemonicString = await ethWallet.getMnemonic();
|
|
||||||
|
|
||||||
//Get private key for given mnemonic
|
|
||||||
String privateKey = getPrivateKey(
|
|
||||||
mnemonicString!,
|
|
||||||
(await ethWallet.getMnemonicPassphrase()) ?? "",
|
|
||||||
);
|
|
||||||
_credentials = web3dart.EthPrivateKey.fromHex(privateKey);
|
|
||||||
|
|
||||||
try {
|
|
||||||
_deployedContract = web3dart.DeployedContract(
|
|
||||||
ContractAbiExtensions.fromJsonList(
|
|
||||||
jsonList: tokenContract.abi!,
|
|
||||||
name: tokenContract.name,
|
|
||||||
),
|
|
||||||
contractAddress,
|
|
||||||
);
|
|
||||||
} catch (_) {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
_sendFunction = _deployedContract.function('transfer');
|
|
||||||
} catch (_) {
|
|
||||||
//====================================================================
|
|
||||||
// final list = List<Map<String, dynamic>>.from(
|
|
||||||
// jsonDecode(tokenContract.abi!) as List);
|
|
||||||
// final functionNames = list.map((e) => e["name"] as String);
|
|
||||||
//
|
//
|
||||||
// if (!functionNames.contains("balanceOf")) {
|
// import 'dart:async';
|
||||||
// list.add(
|
//
|
||||||
// {
|
// import 'package:ethereum_addresses/ethereum_addresses.dart';
|
||||||
// "encoding": "0x70a08231",
|
// import 'package:flutter/widgets.dart';
|
||||||
// "inputs": [
|
// import 'package:http/http.dart';
|
||||||
// {"name": "account", "type": "address"}
|
// import 'package:isar/isar.dart';
|
||||||
|
// import 'package:stackwallet/db/isar/main_db.dart';
|
||||||
|
// import 'package:stackwallet/dto/ethereum/eth_token_tx_dto.dart';
|
||||||
|
// import 'package:stackwallet/dto/ethereum/eth_token_tx_extra_dto.dart';
|
||||||
|
// import 'package:stackwallet/models/balance.dart';
|
||||||
|
// import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||||
|
// import 'package:stackwallet/models/node_model.dart';
|
||||||
|
// import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||||
|
// import 'package:stackwallet/services/ethereum/ethereum_api.dart';
|
||||||
|
// import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
||||||
|
// import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||||
|
// import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||||
|
// import 'package:stackwallet/services/mixins/eth_token_cache.dart';
|
||||||
|
// import 'package:stackwallet/services/node_service.dart';
|
||||||
|
// import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||||
|
// import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
// import 'package:stackwallet/utilities/default_nodes.dart';
|
||||||
|
// import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
// import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||||
|
// import 'package:stackwallet/utilities/eth_commons.dart';
|
||||||
|
// import 'package:stackwallet/utilities/extensions/extensions.dart';
|
||||||
|
// import 'package:stackwallet/utilities/extensions/impl/contract_abi.dart';
|
||||||
|
// import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
|
// import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
// import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||||
|
// import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
||||||
|
// import 'package:tuple/tuple.dart';
|
||||||
|
// import 'package:web3dart/web3dart.dart' as web3dart;
|
||||||
|
//
|
||||||
|
// class EthTokenWallet extends ChangeNotifier {
|
||||||
|
// final EthereumWallet ethWallet;
|
||||||
|
// final TransactionNotificationTracker tracker;
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async {
|
||||||
|
// final txid = txData["txid"] as String;
|
||||||
|
// final addressString = checksumEthereumAddress(txData["address"] as String);
|
||||||
|
// final response = await EthereumAPI.getEthTransactionByHash(txid);
|
||||||
|
//
|
||||||
|
// final transaction = Transaction(
|
||||||
|
// walletId: ethWallet.walletId,
|
||||||
|
// txid: txid,
|
||||||
|
// timestamp: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
|
// type: TransactionType.outgoing,
|
||||||
|
// subType: TransactionSubType.ethToken,
|
||||||
|
// // precision may be lost here hence the following amountString
|
||||||
|
// amount: (txData["recipientAmt"] as Amount).raw.toInt(),
|
||||||
|
// amountString: (txData["recipientAmt"] as Amount).toJsonString(),
|
||||||
|
// fee: (txData["fee"] as Amount).raw.toInt(),
|
||||||
|
// height: null,
|
||||||
|
// isCancelled: false,
|
||||||
|
// isLelantus: false,
|
||||||
|
// otherData: tokenContract.address,
|
||||||
|
// slateId: null,
|
||||||
|
// nonce: (txData["nonce"] as int?) ??
|
||||||
|
// response.value?.nonce.toBigIntFromHex.toInt(),
|
||||||
|
// inputs: [],
|
||||||
|
// outputs: [],
|
||||||
|
// numberOfMessages: null,
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// Address? address = await ethWallet.mainDB.getAddress(
|
||||||
|
// ethWallet.walletId,
|
||||||
|
// addressString,
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// address ??= Address(
|
||||||
|
// walletId: ethWallet.walletId,
|
||||||
|
// value: addressString,
|
||||||
|
// publicKey: [],
|
||||||
|
// derivationIndex: -1,
|
||||||
|
// derivationPath: null,
|
||||||
|
// type: AddressType.ethereum,
|
||||||
|
// subType: AddressSubType.nonWallet,
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// await ethWallet.mainDB.addNewTransactionData(
|
||||||
|
// [
|
||||||
|
// Tuple2(transaction, address),
|
||||||
// ],
|
// ],
|
||||||
// "name": "balanceOf",
|
// ethWallet.walletId,
|
||||||
// "outputs": [
|
|
||||||
// {"name": "val_0", "type": "uint256"}
|
|
||||||
// ],
|
|
||||||
// "signature": "balanceOf(address)",
|
|
||||||
// "type": "function"
|
|
||||||
// },
|
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// if (!functionNames.contains("transfer")) {
|
//
|
||||||
// list.add(
|
//
|
||||||
// {
|
// bool get isRefreshing => _refreshLock;
|
||||||
// "encoding": "0xa9059cbb",
|
//
|
||||||
// "inputs": [
|
// bool _refreshLock = false;
|
||||||
// {"name": "dst", "type": "address"},
|
//
|
||||||
// {"name": "rawAmount", "type": "uint256"}
|
// Future<void> refresh() async {
|
||||||
// ],
|
// if (!_refreshLock) {
|
||||||
// "name": "transfer",
|
// _refreshLock = true;
|
||||||
// "outputs": [
|
// try {
|
||||||
// {"name": "val_0", "type": "bool"}
|
// GlobalEventBus.instance.fire(
|
||||||
// ],
|
// WalletSyncStatusChangedEvent(
|
||||||
// "signature": "transfer(address,uint256)",
|
// WalletSyncStatus.syncing,
|
||||||
// "type": "function"
|
// ethWallet.walletId + tokenContract.address,
|
||||||
// },
|
// coin,
|
||||||
|
// ),
|
||||||
// );
|
// );
|
||||||
|
//
|
||||||
|
// await refreshCachedBalance();
|
||||||
|
// await _refreshTransactions();
|
||||||
|
// } catch (e, s) {
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "Caught exception in ${tokenContract.name} ${ethWallet.info.name} ${ethWallet.walletId} refresh(): $e\n$s",
|
||||||
|
// level: LogLevel.Warning,
|
||||||
|
// );
|
||||||
|
// } finally {
|
||||||
|
// _refreshLock = false;
|
||||||
|
// GlobalEventBus.instance.fire(
|
||||||
|
// WalletSyncStatusChangedEvent(
|
||||||
|
// WalletSyncStatus.synced,
|
||||||
|
// ethWallet.walletId + tokenContract.address,
|
||||||
|
// coin,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// notifyListeners();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Future<List<Transaction>> get transactions => ethWallet.mainDB
|
||||||
|
// .getTransactions(ethWallet.walletId)
|
||||||
|
// .filter()
|
||||||
|
// .otherDataEqualTo(tokenContract.address)
|
||||||
|
// .sortByTimestampDesc()
|
||||||
|
// .findAll();
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
// }
|
// }
|
||||||
//--------------------------------------------------------------------
|
|
||||||
//====================================================================
|
|
||||||
|
|
||||||
// function not found so likely a proxy so we need to fetch the impl
|
|
||||||
//====================================================================
|
|
||||||
// final updatedToken = tokenContract.copyWith(abi: jsonEncode(list));
|
|
||||||
// // Store updated contract
|
|
||||||
// final id = await MainDB.instance.putEthContract(updatedToken);
|
|
||||||
// _tokenContract = updatedToken..id = id;
|
|
||||||
//--------------------------------------------------------------------
|
|
||||||
final contractAddressResponse =
|
|
||||||
await EthereumAPI.getProxyTokenImplementationAddress(
|
|
||||||
contractAddress.hex);
|
|
||||||
|
|
||||||
if (contractAddressResponse.value != null) {
|
|
||||||
_tokenContract = await _updateTokenABI(
|
|
||||||
forContract: tokenContract,
|
|
||||||
usingContractAddress: contractAddressResponse.value!,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw contractAddressResponse.exception!;
|
|
||||||
}
|
|
||||||
//====================================================================
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
_deployedContract = web3dart.DeployedContract(
|
|
||||||
ContractAbiExtensions.fromJsonList(
|
|
||||||
jsonList: tokenContract.abi!,
|
|
||||||
name: tokenContract.name,
|
|
||||||
),
|
|
||||||
contractAddress,
|
|
||||||
);
|
|
||||||
} catch (_) {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
|
|
||||||
_sendFunction = _deployedContract.function('transfer');
|
|
||||||
|
|
||||||
_client = await getEthClient();
|
|
||||||
|
|
||||||
unawaited(refresh());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get isRefreshing => _refreshLock;
|
|
||||||
|
|
||||||
bool _refreshLock = false;
|
|
||||||
|
|
||||||
Future<void> refresh() async {
|
|
||||||
if (!_refreshLock) {
|
|
||||||
_refreshLock = true;
|
|
||||||
try {
|
|
||||||
GlobalEventBus.instance.fire(
|
|
||||||
WalletSyncStatusChangedEvent(
|
|
||||||
WalletSyncStatus.syncing,
|
|
||||||
ethWallet.walletId + tokenContract.address,
|
|
||||||
coin,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await refreshCachedBalance();
|
|
||||||
await _refreshTransactions();
|
|
||||||
} catch (e, s) {
|
|
||||||
Logging.instance.log(
|
|
||||||
"Caught exception in ${tokenContract.name} ${ethWallet.info.name} ${ethWallet.walletId} refresh(): $e\n$s",
|
|
||||||
level: LogLevel.Warning,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
_refreshLock = false;
|
|
||||||
GlobalEventBus.instance.fire(
|
|
||||||
WalletSyncStatusChangedEvent(
|
|
||||||
WalletSyncStatus.synced,
|
|
||||||
ethWallet.walletId + tokenContract.address,
|
|
||||||
coin,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> refreshCachedBalance() async {
|
|
||||||
final response = await EthereumAPI.getWalletTokenBalance(
|
|
||||||
address: _credentials.address.hex,
|
|
||||||
contractAddress: tokenContract.address,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.value != null) {
|
|
||||||
await updateCachedBalance(
|
|
||||||
Balance(
|
|
||||||
total: response.value!,
|
|
||||||
spendable: response.value!,
|
|
||||||
blockedTotal: Amount(
|
|
||||||
rawValue: BigInt.zero,
|
|
||||||
fractionDigits: tokenContract.decimals,
|
|
||||||
),
|
|
||||||
pendingSpendable: Amount(
|
|
||||||
rawValue: BigInt.zero,
|
|
||||||
fractionDigits: tokenContract.decimals,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
notifyListeners();
|
|
||||||
} else {
|
|
||||||
Logging.instance.log(
|
|
||||||
"CachedEthTokenBalance.fetchAndUpdateCachedBalance failed: ${response.exception}",
|
|
||||||
level: LogLevel.Warning,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Transaction>> get transactions => ethWallet.mainDB
|
|
||||||
.getTransactions(ethWallet.walletId)
|
|
||||||
.filter()
|
|
||||||
.otherDataEqualTo(tokenContract.address)
|
|
||||||
.sortByTimestampDesc()
|
|
||||||
.findAll();
|
|
||||||
|
|
||||||
String _addressFromTopic(String topic) =>
|
|
||||||
checksumEthereumAddress("0x${topic.substring(topic.length - 40)}");
|
|
||||||
|
|
||||||
Future<void> _refreshTransactions() async {
|
|
||||||
String addressString =
|
|
||||||
checksumEthereumAddress(await currentReceivingAddress);
|
|
||||||
|
|
||||||
final response = await EthereumAPI.getTokenTransactions(
|
|
||||||
address: addressString,
|
|
||||||
tokenContractAddress: tokenContract.address,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.value == null) {
|
|
||||||
if (response.exception != null &&
|
|
||||||
response.exception!.message
|
|
||||||
.contains("response is empty but status code is 200")) {
|
|
||||||
Logging.instance.log(
|
|
||||||
"No ${tokenContract.name} transfers found for $addressString",
|
|
||||||
level: LogLevel.Info,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw response.exception ??
|
|
||||||
Exception("Failed to fetch token transaction data");
|
|
||||||
}
|
|
||||||
|
|
||||||
// no need to continue if no transactions found
|
|
||||||
if (response.value!.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final response2 = await EthereumAPI.getEthTokenTransactionsByTxids(
|
|
||||||
response.value!.map((e) => e.transactionHash).toSet().toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response2.value == null) {
|
|
||||||
throw response2.exception ??
|
|
||||||
Exception("Failed to fetch token transactions");
|
|
||||||
}
|
|
||||||
final List<Tuple2<EthTokenTxDto, EthTokenTxExtraDTO>> data = [];
|
|
||||||
for (final tokenDto in response.value!) {
|
|
||||||
try {
|
|
||||||
final txExtra = response2.value!.firstWhere(
|
|
||||||
(e) => e.hash == tokenDto.transactionHash,
|
|
||||||
);
|
|
||||||
data.add(
|
|
||||||
Tuple2(
|
|
||||||
tokenDto,
|
|
||||||
txExtra,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (_) {
|
|
||||||
// Server indexing failed for some reason. Instead of hard crashing or
|
|
||||||
// showing no transactions we just skip it here. Not ideal but better
|
|
||||||
// than nothing showing up
|
|
||||||
Logging.instance.log(
|
|
||||||
"Server error: Transaction ${tokenDto.transactionHash} not found.",
|
|
||||||
level: LogLevel.Error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<Tuple2<Transaction, Address?>> txnsData = [];
|
|
||||||
|
|
||||||
for (final tuple in data) {
|
|
||||||
// ignore all non Transfer events (for now)
|
|
||||||
if (tuple.item1.topics[0] == kTransferEventSignature) {
|
|
||||||
final Amount amount;
|
|
||||||
String fromAddress, toAddress;
|
|
||||||
amount = Amount(
|
|
||||||
rawValue: tuple.item1.data.toBigIntFromHex,
|
|
||||||
fractionDigits: tokenContract.decimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
fromAddress = _addressFromTopic(
|
|
||||||
tuple.item1.topics[1],
|
|
||||||
);
|
|
||||||
toAddress = _addressFromTopic(
|
|
||||||
tuple.item1.topics[2],
|
|
||||||
);
|
|
||||||
|
|
||||||
bool isIncoming;
|
|
||||||
bool isSentToSelf = false;
|
|
||||||
if (fromAddress == addressString) {
|
|
||||||
isIncoming = false;
|
|
||||||
if (toAddress == addressString) {
|
|
||||||
isSentToSelf = true;
|
|
||||||
}
|
|
||||||
} else if (toAddress == addressString) {
|
|
||||||
isIncoming = true;
|
|
||||||
} else {
|
|
||||||
// ignore for now I guess since anything here is not reflected in
|
|
||||||
// balance anyways
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// throw Exception("Unknown token transaction found for "
|
|
||||||
// "${ethWallet.walletName} ${ethWallet.walletId}: "
|
|
||||||
// "${tuple.item1.toString()}");
|
|
||||||
}
|
|
||||||
|
|
||||||
final txn = Transaction(
|
|
||||||
walletId: ethWallet.walletId,
|
|
||||||
txid: tuple.item1.transactionHash,
|
|
||||||
timestamp: tuple.item2.timestamp,
|
|
||||||
type:
|
|
||||||
isIncoming ? TransactionType.incoming : TransactionType.outgoing,
|
|
||||||
subType: TransactionSubType.ethToken,
|
|
||||||
amount: amount.raw.toInt(),
|
|
||||||
amountString: amount.toJsonString(),
|
|
||||||
fee: (tuple.item2.gasUsed.raw * tuple.item2.gasPrice.raw).toInt(),
|
|
||||||
height: tuple.item1.blockNumber,
|
|
||||||
isCancelled: false,
|
|
||||||
isLelantus: false,
|
|
||||||
slateId: null,
|
|
||||||
nonce: tuple.item2.nonce,
|
|
||||||
otherData: tuple.item1.address,
|
|
||||||
inputs: [],
|
|
||||||
outputs: [],
|
|
||||||
numberOfMessages: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
Address? transactionAddress = await ethWallet.mainDB
|
|
||||||
.getAddresses(ethWallet.walletId)
|
|
||||||
.filter()
|
|
||||||
.valueEqualTo(toAddress)
|
|
||||||
.findFirst();
|
|
||||||
|
|
||||||
transactionAddress ??= Address(
|
|
||||||
walletId: ethWallet.walletId,
|
|
||||||
value: toAddress,
|
|
||||||
publicKey: [],
|
|
||||||
derivationIndex: isSentToSelf ? 0 : -1,
|
|
||||||
derivationPath: isSentToSelf
|
|
||||||
? (DerivationPath()..value = "$hdPathEthereum/0")
|
|
||||||
: null,
|
|
||||||
type: AddressType.ethereum,
|
|
||||||
subType: isSentToSelf
|
|
||||||
? AddressSubType.receiving
|
|
||||||
: AddressSubType.nonWallet,
|
|
||||||
);
|
|
||||||
|
|
||||||
txnsData.add(Tuple2(txn, transactionAddress));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await ethWallet.mainDB.addNewTransactionData(txnsData, ethWallet.walletId);
|
|
||||||
|
|
||||||
// quick hack to notify manager to call notifyListeners if
|
|
||||||
// transactions changed
|
|
||||||
if (txnsData.isNotEmpty) {
|
|
||||||
GlobalEventBus.instance.fire(
|
|
||||||
UpdatedInBackgroundEvent(
|
|
||||||
"${tokenContract.name} transactions updated/added for: ${ethWallet.walletId} ${ethWallet.info.name}",
|
|
||||||
ethWallet.walletId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validateAddress(String address) {
|
|
||||||
return isValidEthereumAddress(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeModel getCurrentNode() {
|
|
||||||
return NodeService(secureStorageInterface: _secureStore)
|
|
||||||
.getPrimaryNodeFor(coin: coin) ??
|
|
||||||
DefaultNodes.getNodeFor(coin);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<web3dart.Web3Client> getEthClient() async {
|
|
||||||
final node = getCurrentNode();
|
|
||||||
return web3dart.Web3Client(node.host, Client());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,70 +1,70 @@
|
||||||
/*
|
// /*
|
||||||
* This file is part of Stack Wallet.
|
// * This file is part of Stack Wallet.
|
||||||
*
|
// *
|
||||||
* Copyright (c) 2023 Cypher Stack
|
// * Copyright (c) 2023 Cypher Stack
|
||||||
* All Rights Reserved.
|
// * All Rights Reserved.
|
||||||
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
// * The code is distributed under GPLv3 license, see LICENSE file for details.
|
||||||
* Generated by Cypher Stack on 2023-05-26
|
// * Generated by Cypher Stack on 2023-05-26
|
||||||
*
|
// *
|
||||||
*/
|
// */
|
||||||
|
//
|
||||||
import 'package:stackwallet/db/hive/db.dart';
|
// import 'package:stackwallet/db/hive/db.dart';
|
||||||
import 'package:stackwallet/models/balance.dart';
|
// import 'package:stackwallet/models/balance.dart';
|
||||||
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
// import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
// import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
//
|
||||||
abstract class TokenCacheKeys {
|
// abstract class TokenCacheKeys {
|
||||||
static String tokenBalance(String contractAddress) {
|
// static String tokenBalance(String contractAddress) {
|
||||||
return "tokenBalanceCache_$contractAddress";
|
// return "tokenBalanceCache_$contractAddress";
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
mixin EthTokenCache {
|
// mixin EthTokenCache {
|
||||||
late final String _walletId;
|
// late final String _walletId;
|
||||||
late final EthContract _token;
|
// late final EthContract _token;
|
||||||
|
//
|
||||||
void initCache(String walletId, EthContract token) {
|
// void initCache(String walletId, EthContract token) {
|
||||||
_walletId = walletId;
|
// _walletId = walletId;
|
||||||
_token = token;
|
// _token = token;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// token balance cache
|
// // token balance cache
|
||||||
Balance getCachedBalance() {
|
// Balance getCachedBalance() {
|
||||||
final jsonString = DB.instance.get<dynamic>(
|
// final jsonString = DB.instance.get<dynamic>(
|
||||||
boxName: _walletId,
|
// boxName: _walletId,
|
||||||
key: TokenCacheKeys.tokenBalance(_token.address),
|
// key: TokenCacheKeys.tokenBalance(_token.address),
|
||||||
) as String?;
|
// ) as String?;
|
||||||
if (jsonString == null) {
|
// if (jsonString == null) {
|
||||||
return Balance(
|
// return Balance(
|
||||||
total: Amount(
|
// total: Amount(
|
||||||
rawValue: BigInt.zero,
|
// rawValue: BigInt.zero,
|
||||||
fractionDigits: _token.decimals,
|
// fractionDigits: _token.decimals,
|
||||||
),
|
// ),
|
||||||
spendable: Amount(
|
// spendable: Amount(
|
||||||
rawValue: BigInt.zero,
|
// rawValue: BigInt.zero,
|
||||||
fractionDigits: _token.decimals,
|
// fractionDigits: _token.decimals,
|
||||||
),
|
// ),
|
||||||
blockedTotal: Amount(
|
// blockedTotal: Amount(
|
||||||
rawValue: BigInt.zero,
|
// rawValue: BigInt.zero,
|
||||||
fractionDigits: _token.decimals,
|
// fractionDigits: _token.decimals,
|
||||||
),
|
// ),
|
||||||
pendingSpendable: Amount(
|
// pendingSpendable: Amount(
|
||||||
rawValue: BigInt.zero,
|
// rawValue: BigInt.zero,
|
||||||
fractionDigits: _token.decimals,
|
// fractionDigits: _token.decimals,
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
return Balance.fromJson(
|
// return Balance.fromJson(
|
||||||
jsonString,
|
// jsonString,
|
||||||
_token.decimals,
|
// _token.decimals,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Future<void> updateCachedBalance(Balance balance) async {
|
// Future<void> updateCachedBalance(Balance balance) async {
|
||||||
await DB.instance.put<dynamic>(
|
// await DB.instance.put<dynamic>(
|
||||||
boxName: _walletId,
|
// boxName: _walletId,
|
||||||
key: TokenCacheKeys.tokenBalance(_token.address),
|
// key: TokenCacheKeys.tokenBalance(_token.address),
|
||||||
value: balance.toJsonIgnoreCoin(),
|
// value: balance.toJsonIgnoreCoin(),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -12,7 +12,6 @@ import 'package:bip32/bip32.dart' as bip32;
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import "package:hex/hex.dart";
|
import "package:hex/hex.dart";
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
|
||||||
|
@ -71,21 +70,3 @@ String getPrivateKey(String mnemonic, String mnemonicPassphrase) {
|
||||||
|
|
||||||
return HEX.encode(addressAtIndex.privateKey as List<int>);
|
return HEX.encode(addressAtIndex.privateKey as List<int>);
|
||||||
}
|
}
|
||||||
|
|
||||||
Amount estimateFee(int feeRate, int gasLimit, int decimals) {
|
|
||||||
final gweiAmount = feeRate.toDecimal() / (Decimal.ten.pow(9).toDecimal());
|
|
||||||
final fee = gasLimit.toDecimal() *
|
|
||||||
gweiAmount.toDecimal(
|
|
||||||
scaleOnInfinitePrecision: Coin.ethereum.decimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
//Convert gwei to ETH
|
|
||||||
final feeInWei = fee * Decimal.ten.pow(9).toDecimal();
|
|
||||||
final ethAmount = feeInWei / Decimal.ten.pow(decimals).toDecimal();
|
|
||||||
return Amount.fromDecimal(
|
|
||||||
ethAmount.toDecimal(
|
|
||||||
scaleOnInfinitePrecision: Coin.ethereum.decimals,
|
|
||||||
),
|
|
||||||
fractionDigits: decimals,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
93
lib/wallets/isar/models/token_wallet_info.dart
Normal file
93
lib/wallets/isar/models/token_wallet_info.dart
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
import 'package:stackwallet/models/balance.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/isar_id_interface.dart';
|
||||||
|
|
||||||
|
part 'token_wallet_info.g.dart';
|
||||||
|
|
||||||
|
@Collection(accessor: "tokenWalletInfo", inheritance: false)
|
||||||
|
class TokenWalletInfo implements IsarId {
|
||||||
|
@override
|
||||||
|
Id id = Isar.autoIncrement;
|
||||||
|
|
||||||
|
@Index(
|
||||||
|
unique: true,
|
||||||
|
replace: false,
|
||||||
|
composite: [
|
||||||
|
CompositeIndex("tokenAddress"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
final String walletId;
|
||||||
|
|
||||||
|
final String tokenAddress;
|
||||||
|
|
||||||
|
final int tokenFractionDigits;
|
||||||
|
|
||||||
|
final String? cachedBalanceJsonString;
|
||||||
|
|
||||||
|
TokenWalletInfo({
|
||||||
|
required this.walletId,
|
||||||
|
required this.tokenAddress,
|
||||||
|
required this.tokenFractionDigits,
|
||||||
|
this.cachedBalanceJsonString,
|
||||||
|
});
|
||||||
|
|
||||||
|
EthContract getContract(Isar isar) =>
|
||||||
|
isar.ethContracts.where().addressEqualTo(tokenAddress).findFirstSync()!;
|
||||||
|
|
||||||
|
// token balance cache
|
||||||
|
Balance getCachedBalance() {
|
||||||
|
if (cachedBalanceJsonString == null) {
|
||||||
|
return Balance(
|
||||||
|
total: Amount.zeroWith(
|
||||||
|
fractionDigits: tokenFractionDigits,
|
||||||
|
),
|
||||||
|
spendable: Amount.zeroWith(
|
||||||
|
fractionDigits: tokenFractionDigits,
|
||||||
|
),
|
||||||
|
blockedTotal: Amount.zeroWith(
|
||||||
|
fractionDigits: tokenFractionDigits,
|
||||||
|
),
|
||||||
|
pendingSpendable: Amount.zeroWith(
|
||||||
|
fractionDigits: tokenFractionDigits,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Balance.fromJson(
|
||||||
|
cachedBalanceJsonString!,
|
||||||
|
tokenFractionDigits,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateCachedBalance(
|
||||||
|
Balance balance, {
|
||||||
|
required Isar isar,
|
||||||
|
}) async {
|
||||||
|
// // ensure we are updating using the latest entry of this in the db
|
||||||
|
final thisEntry = await isar.tokenWalletInfo
|
||||||
|
.where()
|
||||||
|
.walletIdEqualToTokenAddressNotEqualTo(walletId, tokenAddress)
|
||||||
|
.findFirst();
|
||||||
|
if (thisEntry == null) {
|
||||||
|
throw Exception(
|
||||||
|
"Attempted to update cached token balance before object was saved in db",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await isar.writeTxn(() async {
|
||||||
|
await isar.tokenWalletInfo.deleteByWalletIdTokenAddress(
|
||||||
|
walletId,
|
||||||
|
tokenAddress,
|
||||||
|
);
|
||||||
|
await isar.tokenWalletInfo.put(
|
||||||
|
TokenWalletInfo(
|
||||||
|
walletId: walletId,
|
||||||
|
tokenAddress: tokenAddress,
|
||||||
|
tokenFractionDigits: tokenFractionDigits,
|
||||||
|
cachedBalanceJsonString: balance.toJsonIgnoreCoin(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1162
lib/wallets/isar/models/token_wallet_info.g.dart
Normal file
1162
lib/wallets/isar/models/token_wallet_info.g.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,7 @@
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart';
|
||||||
|
|
||||||
|
final tokenServiceStateProvider = StateProvider<EthTokenWallet?>((ref) => null);
|
||||||
|
|
||||||
|
final pCurrentTokenWallet =
|
||||||
|
Provider<EthTokenWallet?>((ref) => ref.watch(tokenServiceStateProvider));
|
40
lib/wallets/isar/providers/eth/token_balance_provider.dart
Normal file
40
lib/wallets/isar/providers/eth/token_balance_provider.dart
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
import 'package:stackwallet/models/balance.dart';
|
||||||
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/models/token_wallet_info.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/util/watcher.dart';
|
||||||
|
|
||||||
|
final _twiProvider = ChangeNotifierProvider.family<Watcher,
|
||||||
|
({String walletId, String contractAddress})>(
|
||||||
|
(ref, data) {
|
||||||
|
final collection = ref.watch(mainDBProvider).isar.tokenWalletInfo;
|
||||||
|
|
||||||
|
final watcher = Watcher(
|
||||||
|
collection
|
||||||
|
.where()
|
||||||
|
.walletIdTokenAddressEqualTo(data.walletId, data.contractAddress)
|
||||||
|
.findFirstSync()!,
|
||||||
|
collection: collection,
|
||||||
|
);
|
||||||
|
|
||||||
|
ref.onDispose(() => watcher.dispose());
|
||||||
|
|
||||||
|
return watcher;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final pTokenWalletInfo = Provider.family<TokenWalletInfo,
|
||||||
|
({String walletId, String contractAddress})>(
|
||||||
|
(ref, data) {
|
||||||
|
return ref.watch(_twiProvider(data)).value as TokenWalletInfo;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final pTokenBalance =
|
||||||
|
Provider.family<Balance, ({String walletId, String contractAddress})>(
|
||||||
|
(ref, data) {
|
||||||
|
return ref.watch(_twiProvider(data).select(
|
||||||
|
(value) => (value.value as TokenWalletInfo).getCachedBalance()));
|
||||||
|
},
|
||||||
|
);
|
|
@ -82,3 +82,10 @@ final pWalletReceivingAddress = Provider.family<String, String>(
|
||||||
.select((value) => (value.value as WalletInfo).cachedReceivingAddress));
|
.select((value) => (value.value as WalletInfo).cachedReceivingAddress));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final pWalletTokenAddresses = Provider.family<List<String>, String>(
|
||||||
|
(ref, walletId) {
|
||||||
|
return ref.watch(_wiProvider(walletId)
|
||||||
|
.select((value) => (value.value as WalletInfo).tokenContractAddresses));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
|
@ -1022,7 +1022,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
"isCancelled":
|
"isCancelled":
|
||||||
tx.txType == epic_models.TransactionType.TxSentCancelled ||
|
tx.txType == epic_models.TransactionType.TxSentCancelled ||
|
||||||
tx.txType == epic_models.TransactionType.TxReceivedCancelled,
|
tx.txType == epic_models.TransactionType.TxReceivedCancelled,
|
||||||
"anonFees": Amount(
|
"overrideFee": Amount(
|
||||||
rawValue: BigInt.from(fee),
|
rawValue: BigInt.from(fee),
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
).toJsonString(),
|
).toJsonString(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:ethereum_addresses/ethereum_addresses.dart';
|
import 'package:ethereum_addresses/ethereum_addresses.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
@ -56,6 +57,24 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
||||||
return web3.Web3Client(node.host, client);
|
return web3.Web3Client(node.host, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Amount estimateEthFee(int feeRate, int gasLimit, int decimals) {
|
||||||
|
final gweiAmount = feeRate.toDecimal() / (Decimal.ten.pow(9).toDecimal());
|
||||||
|
final fee = gasLimit.toDecimal() *
|
||||||
|
gweiAmount.toDecimal(
|
||||||
|
scaleOnInfinitePrecision: cryptoCurrency.fractionDigits,
|
||||||
|
);
|
||||||
|
|
||||||
|
//Convert gwei to ETH
|
||||||
|
final feeInWei = fee * Decimal.ten.pow(9).toDecimal();
|
||||||
|
final ethAmount = feeInWei / Decimal.ten.pow(decimals).toDecimal();
|
||||||
|
return Amount.fromDecimal(
|
||||||
|
ethAmount.toDecimal(
|
||||||
|
scaleOnInfinitePrecision: cryptoCurrency.fractionDigits,
|
||||||
|
),
|
||||||
|
fractionDigits: decimals,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Private ==============================================
|
// ==================== Private ==============================================
|
||||||
|
|
||||||
Future<void> _initCredentials(
|
Future<void> _initCredentials(
|
||||||
|
@ -118,7 +137,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
||||||
return estimateFee(
|
return estimateEthFee(
|
||||||
feeRate,
|
feeRate,
|
||||||
(cryptoCurrency as Ethereum).gasLimit,
|
(cryptoCurrency as Ethereum).gasLimit,
|
||||||
cryptoCurrency.fractionDigits,
|
cryptoCurrency.fractionDigits,
|
||||||
|
@ -249,7 +268,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
||||||
|
|
||||||
//Calculate fees (GasLimit * gasPrice)
|
//Calculate fees (GasLimit * gasPrice)
|
||||||
// int txFee = element.gasPrice * element.gasUsed;
|
// int txFee = element.gasPrice * element.gasUsed;
|
||||||
Amount txFee = element.gasCost;
|
final Amount txFee = element.gasCost;
|
||||||
final transactionAmount = element.value;
|
final transactionAmount = element.value;
|
||||||
final addressFrom = checksumEthereumAddress(element.from);
|
final addressFrom = checksumEthereumAddress(element.from);
|
||||||
final addressTo = checksumEthereumAddress(element.to);
|
final addressTo = checksumEthereumAddress(element.to);
|
||||||
|
@ -267,7 +286,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// hack epic tx data into inputs and outputs
|
// hack eth tx data into inputs and outputs
|
||||||
final List<OutputV2> outputs = [];
|
final List<OutputV2> outputs = [];
|
||||||
final List<InputV2> inputs = [];
|
final List<InputV2> inputs = [];
|
||||||
|
|
||||||
|
@ -308,7 +327,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
||||||
final otherData = {
|
final otherData = {
|
||||||
"nonce": tuple.item2,
|
"nonce": tuple.item2,
|
||||||
"isCancelled": txFailed,
|
"isCancelled": txFailed,
|
||||||
"anonFees": txFee.toJsonString(),
|
"overrideFee": txFee.toJsonString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
final txn = TransactionV2(
|
final txn = TransactionV2(
|
||||||
|
|
|
@ -500,7 +500,7 @@ class FiroWallet extends Bip39HDWallet
|
||||||
if (anonFees != null) {
|
if (anonFees != null) {
|
||||||
otherData = jsonEncode(
|
otherData = jsonEncode(
|
||||||
{
|
{
|
||||||
"anonFees": anonFees.toJsonString(),
|
"overrideFee": anonFees.toJsonString(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
491
lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart
Normal file
491
lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart
Normal file
|
@ -0,0 +1,491 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:ethereum_addresses/ethereum_addresses.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
import 'package:stackwallet/dto/ethereum/eth_token_tx_dto.dart';
|
||||||
|
import 'package:stackwallet/dto/ethereum/eth_token_tx_extra_dto.dart';
|
||||||
|
import 'package:stackwallet/models/balance.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
||||||
|
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||||
|
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/eth_commons.dart';
|
||||||
|
import 'package:stackwallet/utilities/extensions/extensions.dart';
|
||||||
|
import 'package:stackwallet/utilities/extensions/impl/contract_abi.dart';
|
||||||
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/models/token_wallet_info.dart';
|
||||||
|
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/wallet.dart';
|
||||||
|
import 'package:web3dart/web3dart.dart' as web3dart;
|
||||||
|
|
||||||
|
class EthTokenWallet extends Wallet {
|
||||||
|
@override
|
||||||
|
int get isarTransactionVersion => 2;
|
||||||
|
|
||||||
|
EthTokenWallet(this.ethWallet, this._tokenContract)
|
||||||
|
: super(ethWallet.cryptoCurrency);
|
||||||
|
|
||||||
|
final EthereumWallet ethWallet;
|
||||||
|
|
||||||
|
EthContract get tokenContract => _tokenContract;
|
||||||
|
EthContract _tokenContract;
|
||||||
|
|
||||||
|
late web3dart.DeployedContract _deployedContract;
|
||||||
|
late web3dart.ContractFunction _sendFunction;
|
||||||
|
|
||||||
|
static const _gasLimit = 200000;
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
Future<EthContract> _updateTokenABI({
|
||||||
|
required EthContract forContract,
|
||||||
|
required String usingContractAddress,
|
||||||
|
}) async {
|
||||||
|
final abiResponse = await EthereumAPI.getTokenAbi(
|
||||||
|
name: forContract.name,
|
||||||
|
contractAddress: usingContractAddress,
|
||||||
|
);
|
||||||
|
// Fetch token ABI so we can call token functions
|
||||||
|
if (abiResponse.value != null) {
|
||||||
|
final updatedToken = forContract.copyWith(abi: abiResponse.value!);
|
||||||
|
// Store updated contract
|
||||||
|
final id = await mainDB.putEthContract(updatedToken);
|
||||||
|
return updatedToken..id = id;
|
||||||
|
} else {
|
||||||
|
throw abiResponse.exception!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _addressFromTopic(String topic) =>
|
||||||
|
checksumEthereumAddress("0x${topic.substring(topic.length - 40)}");
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
@override
|
||||||
|
FilterOperation? get changeAddressFilterOperation =>
|
||||||
|
ethWallet.changeAddressFilterOperation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
FilterOperation? get receivingAddressFilterOperation =>
|
||||||
|
ethWallet.receivingAddressFilterOperation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> init() async {
|
||||||
|
await super.init();
|
||||||
|
|
||||||
|
final contractAddress =
|
||||||
|
web3dart.EthereumAddress.fromHex(tokenContract.address);
|
||||||
|
|
||||||
|
if (tokenContract.abi == null) {
|
||||||
|
_tokenContract = await _updateTokenABI(
|
||||||
|
forContract: tokenContract,
|
||||||
|
usingContractAddress: contractAddress.hex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// String? mnemonicString = await ethWallet.getMnemonic();
|
||||||
|
//
|
||||||
|
// //Get private key for given mnemonic
|
||||||
|
// String privateKey = getPrivateKey(
|
||||||
|
// mnemonicString,
|
||||||
|
// (await ethWallet.getMnemonicPassphrase()),
|
||||||
|
// );
|
||||||
|
// _credentials = web3dart.EthPrivateKey.fromHex(privateKey);
|
||||||
|
|
||||||
|
try {
|
||||||
|
_deployedContract = web3dart.DeployedContract(
|
||||||
|
ContractAbiExtensions.fromJsonList(
|
||||||
|
jsonList: tokenContract.abi!,
|
||||||
|
name: tokenContract.name,
|
||||||
|
),
|
||||||
|
contractAddress,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_sendFunction = _deployedContract.function('transfer');
|
||||||
|
} catch (_) {
|
||||||
|
//====================================================================
|
||||||
|
// final list = List<Map<String, dynamic>>.from(
|
||||||
|
// jsonDecode(tokenContract.abi!) as List);
|
||||||
|
// final functionNames = list.map((e) => e["name"] as String);
|
||||||
|
//
|
||||||
|
// if (!functionNames.contains("balanceOf")) {
|
||||||
|
// list.add(
|
||||||
|
// {
|
||||||
|
// "encoding": "0x70a08231",
|
||||||
|
// "inputs": [
|
||||||
|
// {"name": "account", "type": "address"}
|
||||||
|
// ],
|
||||||
|
// "name": "balanceOf",
|
||||||
|
// "outputs": [
|
||||||
|
// {"name": "val_0", "type": "uint256"}
|
||||||
|
// ],
|
||||||
|
// "signature": "balanceOf(address)",
|
||||||
|
// "type": "function"
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!functionNames.contains("transfer")) {
|
||||||
|
// list.add(
|
||||||
|
// {
|
||||||
|
// "encoding": "0xa9059cbb",
|
||||||
|
// "inputs": [
|
||||||
|
// {"name": "dst", "type": "address"},
|
||||||
|
// {"name": "rawAmount", "type": "uint256"}
|
||||||
|
// ],
|
||||||
|
// "name": "transfer",
|
||||||
|
// "outputs": [
|
||||||
|
// {"name": "val_0", "type": "bool"}
|
||||||
|
// ],
|
||||||
|
// "signature": "transfer(address,uint256)",
|
||||||
|
// "type": "function"
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
//====================================================================
|
||||||
|
|
||||||
|
// function not found so likely a proxy so we need to fetch the impl
|
||||||
|
//====================================================================
|
||||||
|
// final updatedToken = tokenContract.copyWith(abi: jsonEncode(list));
|
||||||
|
// // Store updated contract
|
||||||
|
// final id = await MainDB.instance.putEthContract(updatedToken);
|
||||||
|
// _tokenContract = updatedToken..id = id;
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
final contractAddressResponse =
|
||||||
|
await EthereumAPI.getProxyTokenImplementationAddress(
|
||||||
|
contractAddress.hex);
|
||||||
|
|
||||||
|
if (contractAddressResponse.value != null) {
|
||||||
|
_tokenContract = await _updateTokenABI(
|
||||||
|
forContract: tokenContract,
|
||||||
|
usingContractAddress: contractAddressResponse.value!,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw contractAddressResponse.exception!;
|
||||||
|
}
|
||||||
|
//====================================================================
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_deployedContract = web3dart.DeployedContract(
|
||||||
|
ContractAbiExtensions.fromJsonList(
|
||||||
|
jsonList: tokenContract.abi!,
|
||||||
|
name: tokenContract.name,
|
||||||
|
),
|
||||||
|
contractAddress,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sendFunction = _deployedContract.function('transfer');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TxData> prepareSend({required TxData txData}) async {
|
||||||
|
final feeRateType = txData.feeRateType!;
|
||||||
|
int fee = 0;
|
||||||
|
final feeObject = await fees;
|
||||||
|
switch (feeRateType) {
|
||||||
|
case FeeRateType.fast:
|
||||||
|
fee = feeObject.fast;
|
||||||
|
break;
|
||||||
|
case FeeRateType.average:
|
||||||
|
fee = feeObject.medium;
|
||||||
|
break;
|
||||||
|
case FeeRateType.slow:
|
||||||
|
fee = feeObject.slow;
|
||||||
|
break;
|
||||||
|
case FeeRateType.custom:
|
||||||
|
throw UnimplementedError("custom eth token fees");
|
||||||
|
}
|
||||||
|
|
||||||
|
final feeEstimate = await estimateFeeFor(Amount.zero, fee);
|
||||||
|
|
||||||
|
final client = ethWallet.getEthClient();
|
||||||
|
|
||||||
|
final myAddress = (await getCurrentReceivingAddress())!.value;
|
||||||
|
final myWeb3Address = web3dart.EthereumAddress.fromHex(myAddress);
|
||||||
|
|
||||||
|
final nonce = txData.nonce ??
|
||||||
|
await client.getTransactionCount(myWeb3Address,
|
||||||
|
atBlock: const web3dart.BlockNum.pending());
|
||||||
|
|
||||||
|
final amount = txData.recipients!.first.amount;
|
||||||
|
final address = txData.recipients!.first.address;
|
||||||
|
|
||||||
|
final tx = web3dart.Transaction.callContract(
|
||||||
|
contract: _deployedContract,
|
||||||
|
function: _sendFunction,
|
||||||
|
parameters: [web3dart.EthereumAddress.fromHex(address), amount.raw],
|
||||||
|
maxGas: _gasLimit,
|
||||||
|
gasPrice: web3dart.EtherAmount.fromUnitAndValue(
|
||||||
|
web3dart.EtherUnit.wei,
|
||||||
|
fee,
|
||||||
|
),
|
||||||
|
nonce: nonce,
|
||||||
|
);
|
||||||
|
|
||||||
|
return txData.copyWith(
|
||||||
|
fee: feeEstimate,
|
||||||
|
feeInWei: BigInt.from(fee),
|
||||||
|
web3dartTransaction: tx,
|
||||||
|
chainId: await client.getChainId(),
|
||||||
|
nonce: tx.nonce,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TxData> confirmSend({required TxData txData}) async {
|
||||||
|
try {
|
||||||
|
return await ethWallet.confirmSend(txData: txData);
|
||||||
|
} catch (e) {
|
||||||
|
// rethrow to pass error in alert
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
||||||
|
return ethWallet.estimateEthFee(
|
||||||
|
feeRate,
|
||||||
|
_gasLimit,
|
||||||
|
cryptoCurrency.fractionDigits,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<FeeObject> get fees => EthereumAPI.getFees();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> pingCheck() async {
|
||||||
|
return await ethWallet.pingCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> recover({required bool isRescan}) {
|
||||||
|
// TODO: implement recover
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateBalance() async {
|
||||||
|
try {
|
||||||
|
final info = await mainDB.isar.tokenWalletInfo
|
||||||
|
.where()
|
||||||
|
.walletIdTokenAddressEqualTo(walletId, tokenContract.address)
|
||||||
|
.findFirst();
|
||||||
|
final response = await EthereumAPI.getWalletTokenBalance(
|
||||||
|
address: (await getCurrentReceivingAddress())!.value,
|
||||||
|
contractAddress: tokenContract.address,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.value != null && info != null) {
|
||||||
|
await info.updateCachedBalance(
|
||||||
|
Balance(
|
||||||
|
total: response.value!,
|
||||||
|
spendable: response.value!,
|
||||||
|
blockedTotal: Amount(
|
||||||
|
rawValue: BigInt.zero,
|
||||||
|
fractionDigits: tokenContract.decimals,
|
||||||
|
),
|
||||||
|
pendingSpendable: Amount(
|
||||||
|
rawValue: BigInt.zero,
|
||||||
|
fractionDigits: tokenContract.decimals,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
isar: mainDB.isar,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Logging.instance.log(
|
||||||
|
"CachedEthTokenBalance.fetchAndUpdateCachedBalance failed: ${response.exception}",
|
||||||
|
level: LogLevel.Warning,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"$runtimeType wallet failed to update balance: $e\n$s",
|
||||||
|
level: LogLevel.Warning,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateChainHeight() async {
|
||||||
|
await ethWallet.updateChainHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateTransactions() async {
|
||||||
|
try {
|
||||||
|
final String addressString =
|
||||||
|
checksumEthereumAddress((await getCurrentReceivingAddress())!.value);
|
||||||
|
|
||||||
|
final response = await EthereumAPI.getTokenTransactions(
|
||||||
|
address: addressString,
|
||||||
|
tokenContractAddress: tokenContract.address,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.value == null) {
|
||||||
|
if (response.exception != null &&
|
||||||
|
response.exception!.message
|
||||||
|
.contains("response is empty but status code is 200")) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"No ${tokenContract.name} transfers found for $addressString",
|
||||||
|
level: LogLevel.Info,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw response.exception ??
|
||||||
|
Exception("Failed to fetch token transaction data");
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to continue if no transactions found
|
||||||
|
if (response.value!.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final response2 = await EthereumAPI.getEthTokenTransactionsByTxids(
|
||||||
|
response.value!.map((e) => e.transactionHash).toSet().toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response2.value == null) {
|
||||||
|
throw response2.exception ??
|
||||||
|
Exception("Failed to fetch token transactions");
|
||||||
|
}
|
||||||
|
final List<({EthTokenTxDto tx, EthTokenTxExtraDTO extra})> data = [];
|
||||||
|
for (final tokenDto in response.value!) {
|
||||||
|
try {
|
||||||
|
final txExtra = response2.value!.firstWhere(
|
||||||
|
(e) => e.hash == tokenDto.transactionHash,
|
||||||
|
);
|
||||||
|
data.add(
|
||||||
|
(
|
||||||
|
tx: tokenDto,
|
||||||
|
extra: txExtra,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// Server indexing failed for some reason. Instead of hard crashing or
|
||||||
|
// showing no transactions we just skip it here. Not ideal but better
|
||||||
|
// than nothing showing up
|
||||||
|
Logging.instance.log(
|
||||||
|
"Server error: Transaction ${tokenDto.transactionHash} not found.",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<TransactionV2> txns = [];
|
||||||
|
|
||||||
|
for (final tuple in data) {
|
||||||
|
// ignore all non Transfer events (for now)
|
||||||
|
if (tuple.tx.topics[0] == kTransferEventSignature) {
|
||||||
|
final Amount amount;
|
||||||
|
final Amount txFee = tuple.extra.gasUsed * tuple.extra.gasPrice;
|
||||||
|
String fromAddress, toAddress;
|
||||||
|
amount = Amount(
|
||||||
|
rawValue: tuple.tx.data.toBigIntFromHex,
|
||||||
|
fractionDigits: tokenContract.decimals,
|
||||||
|
);
|
||||||
|
|
||||||
|
fromAddress = _addressFromTopic(
|
||||||
|
tuple.tx.topics[1],
|
||||||
|
);
|
||||||
|
toAddress = _addressFromTopic(
|
||||||
|
tuple.tx.topics[2],
|
||||||
|
);
|
||||||
|
|
||||||
|
bool isIncoming;
|
||||||
|
bool isSentToSelf = false;
|
||||||
|
if (fromAddress == addressString) {
|
||||||
|
isIncoming = false;
|
||||||
|
if (toAddress == addressString) {
|
||||||
|
isSentToSelf = true;
|
||||||
|
}
|
||||||
|
} else if (toAddress == addressString) {
|
||||||
|
isIncoming = true;
|
||||||
|
} else {
|
||||||
|
// ignore for now I guess since anything here is not reflected in
|
||||||
|
// balance anyways
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// throw Exception("Unknown token transaction found for "
|
||||||
|
// "${ethWallet.walletName} ${ethWallet.walletId}: "
|
||||||
|
// "${tuple.item1.toString()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
final TransactionType txType;
|
||||||
|
if (isIncoming) {
|
||||||
|
if (fromAddress == toAddress) {
|
||||||
|
txType = TransactionType.sentToSelf;
|
||||||
|
} else {
|
||||||
|
txType = TransactionType.incoming;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
txType = TransactionType.outgoing;
|
||||||
|
}
|
||||||
|
|
||||||
|
final otherData = {
|
||||||
|
"nonce": tuple.extra.nonce,
|
||||||
|
"isCancelled": false,
|
||||||
|
"overrideFee": txFee.toJsonString(),
|
||||||
|
"contractAddress": tuple.tx.address,
|
||||||
|
};
|
||||||
|
|
||||||
|
// hack eth tx data into inputs and outputs
|
||||||
|
final List<OutputV2> outputs = [];
|
||||||
|
final List<InputV2> inputs = [];
|
||||||
|
|
||||||
|
// TODO: ins outs
|
||||||
|
|
||||||
|
final txn = TransactionV2(
|
||||||
|
walletId: walletId,
|
||||||
|
blockHash: tuple.extra.blockHash,
|
||||||
|
hash: tuple.tx.transactionHash,
|
||||||
|
txid: tuple.tx.transactionHash,
|
||||||
|
timestamp: tuple.extra.timestamp,
|
||||||
|
height: tuple.tx.blockNumber,
|
||||||
|
inputs: List.unmodifiable(inputs),
|
||||||
|
outputs: List.unmodifiable(outputs),
|
||||||
|
version: -1,
|
||||||
|
type: txType,
|
||||||
|
subType: TransactionSubType.ethToken,
|
||||||
|
otherData: jsonEncode(otherData),
|
||||||
|
);
|
||||||
|
|
||||||
|
txns.add(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await mainDB.updateOrPutTransactionV2s(txns);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"$runtimeType wallet failed to update transactions: $e\n$s",
|
||||||
|
level: LogLevel.Warning,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateNode() async {
|
||||||
|
await ethWallet.updateNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> updateUTXOs() async {
|
||||||
|
return await ethWallet.updateUTXOs();
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,7 +36,7 @@ abstract class Bip39HDWallet<T extends Bip39HDCurrency> extends Bip39Wallet<T>
|
||||||
derivePathType: DerivePathTypeExt.primaryFor(info.coin),
|
derivePathType: DerivePathTypeExt.primaryFor(info.coin),
|
||||||
);
|
);
|
||||||
|
|
||||||
await mainDB.putAddress(address);
|
await mainDB.updateOrPutAddresses([address]);
|
||||||
await info.updateReceivingAddress(
|
await info.updateReceivingAddress(
|
||||||
newAddress: address.value,
|
newAddress: address.value,
|
||||||
isar: mainDB.isar,
|
isar: mainDB.isar,
|
||||||
|
@ -58,9 +58,7 @@ abstract class Bip39HDWallet<T extends Bip39HDCurrency> extends Bip39Wallet<T>
|
||||||
derivePathType: DerivePathTypeExt.primaryFor(info.coin),
|
derivePathType: DerivePathTypeExt.primaryFor(info.coin),
|
||||||
);
|
);
|
||||||
|
|
||||||
await mainDB.isar.writeTxn(() async {
|
await mainDB.updateOrPutAddresses([address]);
|
||||||
await mainDB.isar.addresses.put(address);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Subclasses may override ========================================
|
// ========== Subclasses may override ========================================
|
||||||
|
|
|
@ -507,7 +507,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
subType: TransactionSubType.sparkSpend,
|
subType: TransactionSubType.sparkSpend,
|
||||||
otherData: jsonEncode(
|
otherData: jsonEncode(
|
||||||
{
|
{
|
||||||
"anonFees": fee.toJsonString(),
|
"overrideFee": fee.toJsonString(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
height: null,
|
height: null,
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/models.dart';
|
import 'package:stackwallet/models/models.dart';
|
||||||
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart';
|
||||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||||
|
@ -14,6 +13,7 @@ import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
||||||
import 'package:stackwallet/widgets/animated_text.dart';
|
import 'package:stackwallet/widgets/animated_text.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
|
@ -61,18 +61,27 @@ class _DesktopFeeDialogState extends ConsumerState<DesktopFeeDialog> {
|
||||||
final fee = await wallet.estimateFeeFor(
|
final fee = await wallet.estimateFeeFor(
|
||||||
amount, MoneroTransactionPriority.fast.raw!);
|
amount, MoneroTransactionPriority.fast.raw!);
|
||||||
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
final Amount fee;
|
||||||
"Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
case FiroType.spark:
|
||||||
await (wallet as FiroWallet).estimateFeeForLelantus(amount);
|
fee =
|
||||||
|
await (wallet as FiroWallet).estimateFeeForSpark(amount);
|
||||||
|
case FiroType.lelantus:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
case FiroType.public:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||||
} else {
|
} else {
|
||||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||||
await wallet.estimateFeeFor(amount, feeRate);
|
await wallet.estimateFeeFor(amount, feeRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||||
ref.read(tokenFeeSessionCacheProvider).fast[amount] = fee;
|
ref.read(tokenFeeSessionCacheProvider).fast[amount] = fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,18 +105,27 @@ class _DesktopFeeDialogState extends ConsumerState<DesktopFeeDialog> {
|
||||||
final fee = await wallet.estimateFeeFor(
|
final fee = await wallet.estimateFeeFor(
|
||||||
amount, MoneroTransactionPriority.regular.raw!);
|
amount, MoneroTransactionPriority.regular.raw!);
|
||||||
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
final Amount fee;
|
||||||
"Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
case FiroType.spark:
|
||||||
await (wallet as FiroWallet).estimateFeeForLelantus(amount);
|
fee =
|
||||||
|
await (wallet as FiroWallet).estimateFeeForSpark(amount);
|
||||||
|
case FiroType.lelantus:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
case FiroType.public:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||||
} else {
|
} else {
|
||||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||||
await wallet.estimateFeeFor(amount, feeRate);
|
await wallet.estimateFeeFor(amount, feeRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||||
ref.read(tokenFeeSessionCacheProvider).average[amount] = fee;
|
ref.read(tokenFeeSessionCacheProvider).average[amount] = fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,18 +149,27 @@ class _DesktopFeeDialogState extends ConsumerState<DesktopFeeDialog> {
|
||||||
final fee = await wallet.estimateFeeFor(
|
final fee = await wallet.estimateFeeFor(
|
||||||
amount, MoneroTransactionPriority.slow.raw!);
|
amount, MoneroTransactionPriority.slow.raw!);
|
||||||
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
final Amount fee;
|
||||||
"Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
case FiroType.spark:
|
||||||
await (wallet as FiroWallet).estimateFeeForLelantus(amount);
|
fee =
|
||||||
|
await (wallet as FiroWallet).estimateFeeForSpark(amount);
|
||||||
|
case FiroType.lelantus:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
case FiroType.public:
|
||||||
|
fee = await (wallet as FiroWallet)
|
||||||
|
.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||||
} else {
|
} else {
|
||||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||||
await wallet.estimateFeeFor(amount, feeRate);
|
await wallet.estimateFeeFor(amount, feeRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||||
ref.read(tokenFeeSessionCacheProvider).slow[amount] = fee;
|
ref.read(tokenFeeSessionCacheProvider).slow[amount] = fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
|
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
|
||||||
import 'package:stackwallet/widgets/expandable.dart';
|
import 'package:stackwallet/widgets/expandable.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
@ -40,17 +39,6 @@ class MasterWalletCard extends ConsumerStatefulWidget {
|
||||||
class _MasterWalletCardState extends ConsumerState<MasterWalletCard> {
|
class _MasterWalletCardState extends ConsumerState<MasterWalletCard> {
|
||||||
final expandableController = ExpandableController();
|
final expandableController = ExpandableController();
|
||||||
final rotateIconController = RotateIconController();
|
final rotateIconController = RotateIconController();
|
||||||
late final List<String> tokenContractAddresses;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
final ethWallet =
|
|
||||||
ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet;
|
|
||||||
|
|
||||||
tokenContractAddresses = ethWallet.info.tokenContractAddresses;
|
|
||||||
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -130,7 +118,7 @@ class _MasterWalletCardState extends ConsumerState<MasterWalletCard> {
|
||||||
popPrevious: true,
|
popPrevious: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
...tokenContractAddresses.map(
|
...ref.watch(pWalletTokenAddresses(widget.walletId)).map(
|
||||||
(e) => Padding(
|
(e) => Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 7,
|
left: 7,
|
||||||
|
|
|
@ -18,16 +18,15 @@ import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
|
||||||
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
|
|
||||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/show_loading.dart';
|
import 'package:stackwallet/utilities/show_loading.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/impl/sub_wallets/eth_token_wallet.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/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
|
@ -55,17 +54,16 @@ class SimpleWalletCard extends ConsumerWidget {
|
||||||
Wallet wallet,
|
Wallet wallet,
|
||||||
EthContract contract,
|
EthContract contract,
|
||||||
) async {
|
) async {
|
||||||
|
final old = ref.read(tokenServiceStateProvider);
|
||||||
|
// exit previous if there is one
|
||||||
|
unawaited(old?.exit());
|
||||||
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
|
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
|
||||||
token: contract,
|
wallet as EthereumWallet,
|
||||||
secureStore: ref.read(secureStoreProvider),
|
contract,
|
||||||
ethWallet: wallet as EthereumWallet,
|
|
||||||
tracker: TransactionNotificationTracker(
|
|
||||||
walletId: walletId,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ref.read(tokenServiceProvider)!.initialize();
|
await ref.read(pCurrentTokenWallet)!.init();
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
await showDialog<void>(
|
await showDialog<void>(
|
||||||
|
|
Loading…
Reference in a new issue