mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-22 19:39:22 +00:00
More WIP eth + tokens
This commit is contained in:
parent
37a164bb8f
commit
9996328126
25 changed files with 1260 additions and 1088 deletions
|
@ -65,6 +65,10 @@ class TransactionV2 {
|
|||
String? get onChainNote => _getFromOtherData(key: "onChainNote") as String?;
|
||||
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) {
|
||||
if (height == null || height! <= 0) return 0;
|
||||
return max(0, currentChainHeight - (height! - 1));
|
||||
|
|
|
@ -22,87 +22,97 @@ const TransactionV2Schema = CollectionSchema(
|
|||
name: r'blockHash',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'hash': PropertySchema(
|
||||
r'contractAddress': PropertySchema(
|
||||
id: 1,
|
||||
name: r'contractAddress',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'hash': PropertySchema(
|
||||
id: 2,
|
||||
name: r'hash',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'height': PropertySchema(
|
||||
id: 2,
|
||||
id: 3,
|
||||
name: r'height',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'inputs': PropertySchema(
|
||||
id: 3,
|
||||
id: 4,
|
||||
name: r'inputs',
|
||||
type: IsarType.objectList,
|
||||
target: r'InputV2',
|
||||
),
|
||||
r'isCancelled': PropertySchema(
|
||||
id: 4,
|
||||
id: 5,
|
||||
name: r'isCancelled',
|
||||
type: IsarType.bool,
|
||||
),
|
||||
r'isEpiccashTransaction': PropertySchema(
|
||||
id: 5,
|
||||
id: 6,
|
||||
name: r'isEpiccashTransaction',
|
||||
type: IsarType.bool,
|
||||
),
|
||||
r'nonce': PropertySchema(
|
||||
id: 7,
|
||||
name: r'nonce',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'numberOfMessages': PropertySchema(
|
||||
id: 6,
|
||||
id: 8,
|
||||
name: r'numberOfMessages',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'onChainNote': PropertySchema(
|
||||
id: 7,
|
||||
id: 9,
|
||||
name: r'onChainNote',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'otherData': PropertySchema(
|
||||
id: 8,
|
||||
id: 10,
|
||||
name: r'otherData',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'outputs': PropertySchema(
|
||||
id: 9,
|
||||
id: 11,
|
||||
name: r'outputs',
|
||||
type: IsarType.objectList,
|
||||
target: r'OutputV2',
|
||||
),
|
||||
r'slateId': PropertySchema(
|
||||
id: 10,
|
||||
id: 12,
|
||||
name: r'slateId',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'subType': PropertySchema(
|
||||
id: 11,
|
||||
id: 13,
|
||||
name: r'subType',
|
||||
type: IsarType.byte,
|
||||
enumMap: _TransactionV2subTypeEnumValueMap,
|
||||
),
|
||||
r'timestamp': PropertySchema(
|
||||
id: 12,
|
||||
id: 14,
|
||||
name: r'timestamp',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'txid': PropertySchema(
|
||||
id: 13,
|
||||
id: 15,
|
||||
name: r'txid',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'type': PropertySchema(
|
||||
id: 14,
|
||||
id: 16,
|
||||
name: r'type',
|
||||
type: IsarType.byte,
|
||||
enumMap: _TransactionV2typeEnumValueMap,
|
||||
),
|
||||
r'version': PropertySchema(
|
||||
id: 15,
|
||||
id: 17,
|
||||
name: r'version',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'walletId': PropertySchema(
|
||||
id: 16,
|
||||
id: 18,
|
||||
name: r'walletId',
|
||||
type: IsarType.string,
|
||||
)
|
||||
|
@ -182,6 +192,12 @@ int _transactionV2EstimateSize(
|
|||
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.inputs.length * 3;
|
||||
{
|
||||
|
@ -229,32 +245,34 @@ void _transactionV2Serialize(
|
|||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeString(offsets[0], object.blockHash);
|
||||
writer.writeString(offsets[1], object.hash);
|
||||
writer.writeLong(offsets[2], object.height);
|
||||
writer.writeString(offsets[1], object.contractAddress);
|
||||
writer.writeString(offsets[2], object.hash);
|
||||
writer.writeLong(offsets[3], object.height);
|
||||
writer.writeObjectList<InputV2>(
|
||||
offsets[3],
|
||||
offsets[4],
|
||||
allOffsets,
|
||||
InputV2Schema.serialize,
|
||||
object.inputs,
|
||||
);
|
||||
writer.writeBool(offsets[4], object.isCancelled);
|
||||
writer.writeBool(offsets[5], object.isEpiccashTransaction);
|
||||
writer.writeLong(offsets[6], object.numberOfMessages);
|
||||
writer.writeString(offsets[7], object.onChainNote);
|
||||
writer.writeString(offsets[8], object.otherData);
|
||||
writer.writeBool(offsets[5], object.isCancelled);
|
||||
writer.writeBool(offsets[6], object.isEpiccashTransaction);
|
||||
writer.writeLong(offsets[7], object.nonce);
|
||||
writer.writeLong(offsets[8], object.numberOfMessages);
|
||||
writer.writeString(offsets[9], object.onChainNote);
|
||||
writer.writeString(offsets[10], object.otherData);
|
||||
writer.writeObjectList<OutputV2>(
|
||||
offsets[9],
|
||||
offsets[11],
|
||||
allOffsets,
|
||||
OutputV2Schema.serialize,
|
||||
object.outputs,
|
||||
);
|
||||
writer.writeString(offsets[10], object.slateId);
|
||||
writer.writeByte(offsets[11], object.subType.index);
|
||||
writer.writeLong(offsets[12], object.timestamp);
|
||||
writer.writeString(offsets[13], object.txid);
|
||||
writer.writeByte(offsets[14], object.type.index);
|
||||
writer.writeLong(offsets[15], object.version);
|
||||
writer.writeString(offsets[16], object.walletId);
|
||||
writer.writeString(offsets[12], object.slateId);
|
||||
writer.writeByte(offsets[13], object.subType.index);
|
||||
writer.writeLong(offsets[14], object.timestamp);
|
||||
writer.writeString(offsets[15], object.txid);
|
||||
writer.writeByte(offsets[16], object.type.index);
|
||||
writer.writeLong(offsets[17], object.version);
|
||||
writer.writeString(offsets[18], object.walletId);
|
||||
}
|
||||
|
||||
TransactionV2 _transactionV2Deserialize(
|
||||
|
@ -265,32 +283,32 @@ TransactionV2 _transactionV2Deserialize(
|
|||
) {
|
||||
final object = TransactionV2(
|
||||
blockHash: reader.readStringOrNull(offsets[0]),
|
||||
hash: reader.readString(offsets[1]),
|
||||
height: reader.readLongOrNull(offsets[2]),
|
||||
hash: reader.readString(offsets[2]),
|
||||
height: reader.readLongOrNull(offsets[3]),
|
||||
inputs: reader.readObjectList<InputV2>(
|
||||
offsets[3],
|
||||
offsets[4],
|
||||
InputV2Schema.deserialize,
|
||||
allOffsets,
|
||||
InputV2(),
|
||||
) ??
|
||||
[],
|
||||
otherData: reader.readStringOrNull(offsets[8]),
|
||||
otherData: reader.readStringOrNull(offsets[10]),
|
||||
outputs: reader.readObjectList<OutputV2>(
|
||||
offsets[9],
|
||||
offsets[11],
|
||||
OutputV2Schema.deserialize,
|
||||
allOffsets,
|
||||
OutputV2(),
|
||||
) ??
|
||||
[],
|
||||
subType:
|
||||
_TransactionV2subTypeValueEnumMap[reader.readByteOrNull(offsets[11])] ??
|
||||
_TransactionV2subTypeValueEnumMap[reader.readByteOrNull(offsets[13])] ??
|
||||
TransactionSubType.none,
|
||||
timestamp: reader.readLong(offsets[12]),
|
||||
txid: reader.readString(offsets[13]),
|
||||
type: _TransactionV2typeValueEnumMap[reader.readByteOrNull(offsets[14])] ??
|
||||
timestamp: reader.readLong(offsets[14]),
|
||||
txid: reader.readString(offsets[15]),
|
||||
type: _TransactionV2typeValueEnumMap[reader.readByteOrNull(offsets[16])] ??
|
||||
TransactionType.outgoing,
|
||||
version: reader.readLong(offsets[15]),
|
||||
walletId: reader.readString(offsets[16]),
|
||||
version: reader.readLong(offsets[17]),
|
||||
walletId: reader.readString(offsets[18]),
|
||||
);
|
||||
object.id = id;
|
||||
return object;
|
||||
|
@ -306,10 +324,12 @@ P _transactionV2DeserializeProp<P>(
|
|||
case 0:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 1:
|
||||
return (reader.readString(offset)) as P;
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
return (reader.readString(offset)) as P;
|
||||
case 3:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 4:
|
||||
return (reader.readObjectList<InputV2>(
|
||||
offset,
|
||||
InputV2Schema.deserialize,
|
||||
|
@ -317,17 +337,19 @@ P _transactionV2DeserializeProp<P>(
|
|||
InputV2(),
|
||||
) ??
|
||||
[]) as P;
|
||||
case 4:
|
||||
return (reader.readBool(offset)) as P;
|
||||
case 5:
|
||||
return (reader.readBool(offset)) as P;
|
||||
case 6:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
return (reader.readBool(offset)) as P;
|
||||
case 7:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 8:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 9:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 10:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 11:
|
||||
return (reader.readObjectList<OutputV2>(
|
||||
offset,
|
||||
OutputV2Schema.deserialize,
|
||||
|
@ -335,22 +357,22 @@ P _transactionV2DeserializeProp<P>(
|
|||
OutputV2(),
|
||||
) ??
|
||||
[]) as P;
|
||||
case 10:
|
||||
case 12:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 11:
|
||||
case 13:
|
||||
return (_TransactionV2subTypeValueEnumMap[
|
||||
reader.readByteOrNull(offset)] ??
|
||||
TransactionSubType.none) as P;
|
||||
case 12:
|
||||
return (reader.readLong(offset)) as P;
|
||||
case 13:
|
||||
return (reader.readString(offset)) as P;
|
||||
case 14:
|
||||
return (reader.readLong(offset)) as P;
|
||||
case 15:
|
||||
return (reader.readString(offset)) as P;
|
||||
case 16:
|
||||
return (_TransactionV2typeValueEnumMap[reader.readByteOrNull(offset)] ??
|
||||
TransactionType.outgoing) as P;
|
||||
case 15:
|
||||
case 17:
|
||||
return (reader.readLong(offset)) as P;
|
||||
case 16:
|
||||
case 18:
|
||||
return (reader.readString(offset)) as P;
|
||||
default:
|
||||
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(
|
||||
String value, {
|
||||
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>
|
||||
numberOfMessagesIsNull() {
|
||||
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() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
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>
|
||||
sortByNumberOfMessages() {
|
||||
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() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
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>
|
||||
thenByNumberOfMessages() {
|
||||
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(
|
||||
{bool caseSensitive = true}) {
|
||||
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>
|
||||
distinctByNumberOfMessages() {
|
||||
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() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
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>
|
||||
numberOfMessagesProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
|
|
|
@ -20,7 +20,6 @@ import 'package:stackwallet/models/isar/models/transaction_note.dart';
|
|||
import 'package:stackwallet/notifications/show_flush_bar.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/token_view/token_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/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/text_styles.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/models/tx_data.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
||||
|
@ -202,7 +202,7 @@ class _ConfirmTransactionViewState
|
|||
}
|
||||
|
||||
if (widget.isTokenTx) {
|
||||
unawaited(ref.read(tokenServiceProvider)!.refresh());
|
||||
unawaited(ref.read(pCurrentTokenWallet)!.refresh());
|
||||
} else {
|
||||
unawaited(wallet.refresh());
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ class _ConfirmTransactionViewState
|
|||
final String unit;
|
||||
if (widget.isTokenTx) {
|
||||
unit = ref.watch(
|
||||
tokenServiceProvider.select((value) => value!.tokenContract.symbol));
|
||||
pCurrentTokenWallet.select((value) => value!.tokenContract.symbol));
|
||||
} else {
|
||||
unit = coin.ticker;
|
||||
}
|
||||
|
@ -518,7 +518,7 @@ class _ConfirmTransactionViewState
|
|||
ref.watch(pAmountFormatter(coin)).format(
|
||||
amountWithoutChange,
|
||||
ethContract: ref
|
||||
.watch(tokenServiceProvider)
|
||||
.watch(pCurrentTokenWallet)
|
||||
?.tokenContract,
|
||||
),
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
|
@ -708,7 +708,7 @@ class _ConfirmTransactionViewState
|
|||
priceAnd24hChangeNotifierProvider)
|
||||
.getTokenPrice(
|
||||
ref
|
||||
.read(tokenServiceProvider)!
|
||||
.read(pCurrentTokenWallet)!
|
||||
.tokenContract
|
||||
.address,
|
||||
)
|
||||
|
@ -737,7 +737,7 @@ class _ConfirmTransactionViewState
|
|||
ref.watch(pAmountFormatter(coin)).format(
|
||||
amountWithoutChange,
|
||||
ethContract: ref
|
||||
.read(tokenServiceProvider)
|
||||
.read(pCurrentTokenWallet)
|
||||
?.tokenContract),
|
||||
style: STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
|
|
|
@ -12,7 +12,6 @@ import 'package:cw_core/monero_transaction_priority.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.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/ui/fee_rate_type_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/logger.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/wallet/impl/firo_wallet.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
|
@ -110,8 +110,8 @@ class _TransactionFeeSelectionSheetState
|
|||
await wallet.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||
}
|
||||
}
|
||||
|
@ -144,8 +144,8 @@ class _TransactionFeeSelectionSheetState
|
|||
await wallet.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||
}
|
||||
}
|
||||
|
@ -178,8 +178,8 @@ class _TransactionFeeSelectionSheetState
|
|||
await wallet.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||
}
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ class _TransactionFeeSelectionSheetState
|
|||
),
|
||||
FutureBuilder(
|
||||
future: widget.isToken
|
||||
? ref.read(tokenServiceProvider)!.fees
|
||||
? ref.read(pCurrentTokenWallet)!.fees
|
||||
: wallet.fees,
|
||||
builder: (context, AsyncSnapshot<FeeObject> snapshot) {
|
||||
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/sub_widgets/building_transaction_dialog.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/ui/fee_rate_type_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/text_styles.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/models/tx_data.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
|
@ -353,7 +354,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
}
|
||||
|
||||
Future<String> calculateFees() async {
|
||||
final wallet = ref.read(tokenServiceProvider)!;
|
||||
final wallet = ref.read(pCurrentTokenWallet)!;
|
||||
final feeObject = await wallet.fees;
|
||||
|
||||
late final int feeRate;
|
||||
|
@ -372,7 +373,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
feeRate = -1;
|
||||
}
|
||||
|
||||
final Amount fee = wallet.estimateFeeFor(feeRate);
|
||||
final Amount fee = await wallet.estimateFeeFor(Amount.zero, feeRate);
|
||||
cachedFees = ref.read(pAmountFormatter(coin)).format(
|
||||
fee,
|
||||
withUnitName: true,
|
||||
|
@ -389,7 +390,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
const Duration(milliseconds: 100),
|
||||
);
|
||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||
|
||||
final Amount amount = _amountToSend!;
|
||||
|
||||
|
@ -711,8 +712,11 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
.watch(pAmountFormatter(coin))
|
||||
.format(
|
||||
ref
|
||||
.read(tokenServiceProvider)!
|
||||
.balance
|
||||
.read(pTokenBalance((
|
||||
walletId: widget.walletId,
|
||||
contractAddress:
|
||||
tokenContract.address,
|
||||
)))
|
||||
.spendable,
|
||||
ethContract: tokenContract,
|
||||
withUnitName: false,
|
||||
|
@ -729,18 +733,16 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
ref
|
||||
.watch(pAmountFormatter(coin))
|
||||
.format(
|
||||
ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
(value) => value!
|
||||
.balance.spendable,
|
||||
),
|
||||
),
|
||||
ethContract: ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
(value) =>
|
||||
value!.tokenContract,
|
||||
),
|
||||
),
|
||||
ref
|
||||
.watch(pTokenBalance((
|
||||
walletId:
|
||||
widget.walletId,
|
||||
contractAddress:
|
||||
tokenContract
|
||||
.address,
|
||||
)))
|
||||
.spendable,
|
||||
ethContract: tokenContract,
|
||||
),
|
||||
style:
|
||||
STextStyles.titleBold12(context)
|
||||
|
@ -750,7 +752,13 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
textAlign: TextAlign.right,
|
||||
),
|
||||
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,
|
||||
).fiatString(
|
||||
locale: locale,
|
||||
|
|
|
@ -8,17 +8,16 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.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/providers/db/main_db_provider.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.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/utilities/amount/amount_formatter.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
|
@ -26,9 +25,11 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/utilities/show_loading.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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/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/dialogs/basic_dialog.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
|
||||
|
@ -58,7 +59,7 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
|
|||
WidgetRef ref,
|
||||
) async {
|
||||
try {
|
||||
await ref.read(tokenServiceProvider)!.initialize();
|
||||
await ref.read(pCurrentTokenWallet)!.init();
|
||||
return true;
|
||||
} catch (_) {
|
||||
await showDialog<void>(
|
||||
|
@ -84,14 +85,12 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
|
|||
}
|
||||
|
||||
void _onPressed() async {
|
||||
final old = ref.read(tokenServiceStateProvider);
|
||||
// exit previous if there is one
|
||||
unawaited(old?.exit());
|
||||
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
|
||||
token: widget.token,
|
||||
secureStore: ref.read(secureStoreProvider),
|
||||
ethWallet:
|
||||
ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet,
|
||||
tracker: TransactionNotificationTracker(
|
||||
walletId: widget.walletId,
|
||||
),
|
||||
ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet,
|
||||
widget.token,
|
||||
);
|
||||
|
||||
final success = await showLoading<bool>(
|
||||
|
|
|
@ -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/receive_view/receive_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/providers/global/locale_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/enums/coin_enum.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/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
|
@ -51,9 +52,9 @@ class TokenSummary extends ConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final token =
|
||||
ref.watch(tokenServiceProvider.select((value) => value!.tokenContract));
|
||||
final balance =
|
||||
ref.watch(tokenServiceProvider.select((value) => value!.balance));
|
||||
ref.watch(pCurrentTokenWallet.select((value) => value!.tokenContract));
|
||||
final balance = ref.watch(
|
||||
pTokenBalance((walletId: walletId, contractAddress: token.address)));
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
|
@ -157,7 +158,7 @@ class TokenSummary extends ConsumerWidget {
|
|||
walletId: walletId,
|
||||
initialSyncStatus: initialSyncStatus,
|
||||
tokenContractAddress: ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
pCurrentTokenWallet.select(
|
||||
(value) => value!.tokenContract.address,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -12,25 +12,18 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
|
||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.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/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/route_generator.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.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/wallets/isar/providers/wallet_info_provider.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.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 {
|
||||
const TokenTransactionsList({
|
||||
|
@ -49,7 +42,10 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
|||
late final int minConfirms;
|
||||
|
||||
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 {
|
||||
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
|
||||
void initState() {
|
||||
minConfirms = ref
|
||||
|
@ -213,21 +76,44 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
|||
.getWallet(widget.walletId)
|
||||
.cryptoCurrency
|
||||
.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();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final wallet =
|
||||
ref.watch(pWallets.select((value) => value.getWallet(widget.walletId)));
|
||||
|
||||
return FutureBuilder(
|
||||
future: ref
|
||||
.watch(tokenServiceProvider.select((value) => value!.transactions)),
|
||||
builder: (fbContext, AsyncSnapshot<List<Transaction>> snapshot) {
|
||||
future: _query.findAll(),
|
||||
builder: (fbContext, AsyncSnapshot<List<TransactionV2>> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_transactions2 = snapshot.data!;
|
||||
_transactions = snapshot.data!;
|
||||
_hasLoaded = true;
|
||||
}
|
||||
if (!_hasLoaded) {
|
||||
|
@ -246,35 +132,34 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
|||
],
|
||||
);
|
||||
}
|
||||
if (_transactions2.isEmpty) {
|
||||
if (_transactions.isEmpty) {
|
||||
return const NoTransActionsFound();
|
||||
} else {
|
||||
_transactions2.sort((a, b) => b.timestamp - a.timestamp);
|
||||
_transactions.sort((a, b) => b.timestamp - a.timestamp);
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
if (!ref.read(tokenServiceProvider)!.isRefreshing) {
|
||||
unawaited(ref.read(tokenServiceProvider)!.refresh());
|
||||
if (!ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked) {
|
||||
unawaited(ref.read(pCurrentTokenWallet)!.refresh());
|
||||
}
|
||||
},
|
||||
child: Util.isDesktop
|
||||
? ListView.separated(
|
||||
itemBuilder: (context, index) {
|
||||
BorderRadius? radius;
|
||||
if (_transactions2.length == 1) {
|
||||
if (_transactions.length == 1) {
|
||||
radius = BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
);
|
||||
} else if (index == _transactions2.length - 1) {
|
||||
} else if (index == _transactions.length - 1) {
|
||||
radius = _borderRadiusLast;
|
||||
} else if (index == 0) {
|
||||
radius = _borderRadiusFirst;
|
||||
}
|
||||
final tx = _transactions2[index];
|
||||
return itemBuilder(
|
||||
context,
|
||||
tx,
|
||||
radius,
|
||||
wallet.info.coin,
|
||||
final tx = _transactions[index];
|
||||
return TxListItem(
|
||||
tx: tx,
|
||||
coin: wallet.info.coin,
|
||||
radius: radius,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
|
@ -286,27 +171,26 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
|||
.background,
|
||||
);
|
||||
},
|
||||
itemCount: _transactions2.length,
|
||||
itemCount: _transactions.length,
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: _transactions2.length,
|
||||
itemCount: _transactions.length,
|
||||
itemBuilder: (context, index) {
|
||||
BorderRadius? radius;
|
||||
if (_transactions2.length == 1) {
|
||||
if (_transactions.length == 1) {
|
||||
radius = BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
);
|
||||
} else if (index == _transactions2.length - 1) {
|
||||
} else if (index == _transactions.length - 1) {
|
||||
radius = _borderRadiusLast;
|
||||
} else if (index == 0) {
|
||||
radius = _borderRadiusFirst;
|
||||
}
|
||||
final tx = _transactions2[index];
|
||||
return itemBuilder(
|
||||
context,
|
||||
tx,
|
||||
radius,
|
||||
wallet.info.coin,
|
||||
final tx = _transactions[index];
|
||||
return TxListItem(
|
||||
tx: tx,
|
||||
coin: wallet.info.coin,
|
||||
radius: radius,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -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_transaction_list_widget.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/services/ethereum/ethereum_token_service.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.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/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.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/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.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
|
||||
class TokenView extends ConsumerStatefulWidget {
|
||||
const TokenView({
|
||||
|
@ -56,7 +52,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
initialSyncStatus = ref.read(tokenServiceProvider)!.isRefreshing
|
||||
initialSyncStatus = ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked
|
||||
? WalletSyncStatus.syncing
|
||||
: WalletSyncStatus.synced;
|
||||
super.initState();
|
||||
|
@ -105,7 +101,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
|||
children: [
|
||||
EthTokenIcon(
|
||||
contractAddress: ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
pCurrentTokenWallet.select(
|
||||
(value) => value!.tokenContract.address,
|
||||
),
|
||||
),
|
||||
|
@ -116,7 +112,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
|||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
ref.watch(tokenServiceProvider
|
||||
ref.watch(pCurrentTokenWallet
|
||||
.select((value) => value!.tokenContract.name)),
|
||||
style: STextStyles.navBarTitle(context),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
@ -145,7 +141,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
|||
Navigator.of(context).pushNamed(
|
||||
TokenContractDetailsView.routeName,
|
||||
arguments: Tuple2(
|
||||
ref.watch(tokenServiceProvider
|
||||
ref.watch(pCurrentTokenWallet
|
||||
.select((value) => value!.tokenContract.address)),
|
||||
widget.walletId,
|
||||
),
|
||||
|
@ -190,7 +186,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
|||
text: "See all",
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
AllTransactionsView.routeName,
|
||||
AllTransactionsV2View.routeName,
|
||||
arguments: (
|
||||
walletId: widget.walletId,
|
||||
isTokens: true,
|
||||
|
|
|
@ -13,13 +13,13 @@ import 'dart:async';
|
|||
import 'package:event_bus/event_bus.dart';
|
||||
import 'package:flutter/material.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/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/constants.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';
|
||||
|
||||
/// [eventBus] should only be set during testing
|
||||
|
@ -140,8 +140,8 @@ class _RefreshButtonState extends ConsumerState<WalletRefreshButton> {
|
|||
wallet.refresh().then((_) => _spinController.stop?.call());
|
||||
}
|
||||
} else {
|
||||
if (!ref.read(tokenServiceProvider)!.isRefreshing) {
|
||||
ref.read(tokenServiceProvider)!.refresh();
|
||||
if (!ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked) {
|
||||
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/transaction_filter.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/transaction_views/transaction_details_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({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
this.isTokens = false,
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/allTransactions";
|
||||
|
||||
final String walletId;
|
||||
final bool isTokens;
|
||||
|
||||
@override
|
||||
ConsumerState<AllTransactionsView> createState() =>
|
||||
|
@ -487,41 +484,25 @@ class _TransactionDetailsViewState extends ConsumerState<AllTransactionsView> {
|
|||
final criteria =
|
||||
ref.watch(transactionFilterProvider.state).state;
|
||||
|
||||
//todo: check if print needed
|
||||
// debugPrint("Consumer build called");
|
||||
|
||||
final WhereClause ww;
|
||||
|
||||
return FutureBuilder(
|
||||
future: widget.isTokens
|
||||
? ref
|
||||
.watch(mainDBProvider)
|
||||
.getTransactions(walletId)
|
||||
.filter()
|
||||
.otherDataEqualTo(ref
|
||||
.watch(tokenServiceProvider)!
|
||||
.tokenContract
|
||||
.address)
|
||||
.sortByTimestampDesc()
|
||||
.findAll()
|
||||
: ref.watch(mainDBProvider).isar.transactions.buildQuery<
|
||||
Transaction>(
|
||||
whereClauses: [
|
||||
IndexWhereClause.equalTo(
|
||||
indexName: 'walletId',
|
||||
value: [widget.walletId],
|
||||
)
|
||||
],
|
||||
// TODO: [prio=high] add filters to wallet or cryptocurrency class
|
||||
// filter: [
|
||||
// // todo
|
||||
// ],
|
||||
sortBy: [
|
||||
const SortProperty(
|
||||
property: "timestamp",
|
||||
sort: Sort.desc,
|
||||
),
|
||||
]).findAll(),
|
||||
future: ref.watch(mainDBProvider).isar.transactions.buildQuery<
|
||||
Transaction>(
|
||||
whereClauses: [
|
||||
IndexWhereClause.equalTo(
|
||||
indexName: 'walletId',
|
||||
value: [widget.walletId],
|
||||
)
|
||||
],
|
||||
// TODO: [prio=high] add filters to wallet or cryptocurrency class
|
||||
// filter: [
|
||||
// // todo
|
||||
// ],
|
||||
sortBy: [
|
||||
const SortProperty(
|
||||
property: "timestamp",
|
||||
sort: Sort.desc,
|
||||
),
|
||||
]).findAll(),
|
||||
builder: (_, AsyncSnapshot<List<Transaction>> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
|
|
|
@ -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/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/token_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_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/utilities/assets.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/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||
|
@ -57,7 +57,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
initialSyncStatus = ref.read(tokenServiceProvider)!.isRefreshing
|
||||
initialSyncStatus = ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked
|
||||
? WalletSyncStatus.syncing
|
||||
: WalletSyncStatus.synced;
|
||||
super.initState();
|
||||
|
@ -114,7 +114,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
|||
children: [
|
||||
EthTokenIcon(
|
||||
contractAddress: ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
pCurrentTokenWallet.select(
|
||||
(value) => value!.tokenContract.address,
|
||||
),
|
||||
),
|
||||
|
@ -125,7 +125,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
|||
),
|
||||
Text(
|
||||
ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
pCurrentTokenWallet.select(
|
||||
(value) => value!.tokenContract.name,
|
||||
),
|
||||
),
|
||||
|
@ -153,7 +153,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
|||
children: [
|
||||
EthTokenIcon(
|
||||
contractAddress: ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
pCurrentTokenWallet.select(
|
||||
(value) => value!.tokenContract.address,
|
||||
),
|
||||
),
|
||||
|
@ -241,7 +241,7 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
|
|||
child: MyWallet(
|
||||
walletId: widget.walletId,
|
||||
contractAddress: ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
pCurrentTokenWallet.select(
|
||||
(value) => value!.tokenContract.address,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -15,7 +15,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/models/models.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/ui/fee_rate_type_state_provider.dart';
|
||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||
|
@ -27,6 +26,7 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.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/wallet/impl/firo_wallet.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
|
@ -103,8 +103,8 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
|
|||
await wallet.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||
ref.read(tokenFeeSessionCacheProvider).fast[amount] = fee;
|
||||
}
|
||||
}
|
||||
|
@ -147,8 +147,8 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
|
|||
await wallet.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||
ref.read(tokenFeeSessionCacheProvider).average[amount] = fee;
|
||||
}
|
||||
}
|
||||
|
@ -191,8 +191,8 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
|
|||
await wallet.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||
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/notifications/show_flush_bar.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/providers.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/text_styles.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/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||
|
@ -307,7 +307,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
|||
children: [
|
||||
Text(
|
||||
"Your ${widget.contractAddress == null ? coin.ticker : ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
pCurrentTokenWallet.select(
|
||||
(value) => value!.tokenContract.symbol,
|
||||
),
|
||||
)} SPARK address",
|
||||
|
@ -398,7 +398,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
|||
children: [
|
||||
Text(
|
||||
"Your ${widget.contractAddress == null ? coin.ticker : ref.watch(
|
||||
tokenServiceProvider.select(
|
||||
pCurrentTokenWallet.select(
|
||||
(value) => value!.tokenContract.symbol,
|
||||
),
|
||||
)} 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/pages/send_view/confirm_transaction_view.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/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';
|
||||
|
@ -39,6 +38,8 @@ import 'package:stackwallet/utilities/logger.dart';
|
|||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
|
@ -103,10 +104,15 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
late VoidCallback onCryptoAmountChanged;
|
||||
|
||||
Future<void> previewSend() async {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||
|
||||
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
|
||||
if (amount == availableBalance) {
|
||||
|
@ -214,7 +220,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: BuildingTransactionDialog(
|
||||
coin: tokenWallet.coin,
|
||||
coin: tokenWallet.cryptoCurrency.coin,
|
||||
onCancel: () {
|
||||
wasCancelled = true;
|
||||
|
||||
|
@ -389,11 +395,11 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
_amountToSend = cryptoAmount.contains(",")
|
||||
? Decimal.parse(cryptoAmount.replaceFirst(",", ".")).toAmount(
|
||||
fractionDigits:
|
||||
ref.read(tokenServiceProvider)!.tokenContract.decimals,
|
||||
ref.read(pCurrentTokenWallet)!.tokenContract.decimals,
|
||||
)
|
||||
: Decimal.parse(cryptoAmount).toAmount(
|
||||
fractionDigits:
|
||||
ref.read(tokenServiceProvider)!.tokenContract.decimals,
|
||||
ref.read(pCurrentTokenWallet)!.tokenContract.decimals,
|
||||
);
|
||||
if (_cachedAmountToSend != null &&
|
||||
_cachedAmountToSend == _amountToSend) {
|
||||
|
@ -406,7 +412,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
final price = ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.getTokenPrice(
|
||||
ref.read(tokenServiceProvider)!.tokenContract.address,
|
||||
ref.read(pCurrentTokenWallet)!.tokenContract.address,
|
||||
)
|
||||
.item1;
|
||||
|
||||
|
@ -485,7 +491,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
if (results["amount"] != null) {
|
||||
final amount = Decimal.parse(results["amount"]!).toAmount(
|
||||
fractionDigits:
|
||||
ref.read(tokenServiceProvider)!.tokenContract.decimals,
|
||||
ref.read(pCurrentTokenWallet)!.tokenContract.decimals,
|
||||
);
|
||||
cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format(
|
||||
amount,
|
||||
|
@ -543,7 +549,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
|
||||
void fiatTextFieldOnChanged(String baseAmountString) {
|
||||
final int tokenDecimals =
|
||||
ref.read(tokenServiceProvider)!.tokenContract.decimals;
|
||||
ref.read(pCurrentTokenWallet)!.tokenContract.decimals;
|
||||
|
||||
if (baseAmountString.isNotEmpty &&
|
||||
baseAmountString != "." &&
|
||||
|
@ -556,7 +562,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
final Decimal _price = ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.getTokenPrice(
|
||||
ref.read(tokenServiceProvider)!.tokenContract.address,
|
||||
ref.read(pCurrentTokenWallet)!.tokenContract.address,
|
||||
)
|
||||
.item1;
|
||||
|
||||
|
@ -579,7 +585,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
final amountString = ref.read(pAmountFormatter(coin)).format(
|
||||
_amountToSend!,
|
||||
withUnitName: false,
|
||||
ethContract: ref.read(tokenServiceProvider)!.tokenContract,
|
||||
ethContract: ref.read(pCurrentTokenWallet)!.tokenContract,
|
||||
);
|
||||
|
||||
_cryptoAmountChangeLock = true;
|
||||
|
@ -597,12 +603,14 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
|
||||
Future<void> sendAllTapped() async {
|
||||
cryptoAmountController.text = ref
|
||||
.read(tokenServiceProvider)!
|
||||
.balance
|
||||
.read(pTokenBalance((
|
||||
walletId: walletId,
|
||||
contractAddress: ref.read(pCurrentTokenWallet)!.tokenContract.address
|
||||
)))
|
||||
.spendable
|
||||
.decimal
|
||||
.toStringAsFixed(
|
||||
ref.read(tokenServiceProvider)!.tokenContract.decimals,
|
||||
ref.read(pCurrentTokenWallet)!.tokenContract.decimals,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -686,7 +694,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
|||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
final tokenContract = ref.watch(tokenServiceProvider)!.tokenContract;
|
||||
final tokenContract = ref.watch(pCurrentTokenWallet)!.tokenContract;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.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_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.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/wallet_balance_toggle_state.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';
|
||||
|
||||
class DesktopWalletSummary extends ConsumerStatefulWidget {
|
||||
|
@ -70,8 +71,7 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
|
|||
.watch(prefsChangeNotifierProvider.select((value) => value.currency));
|
||||
|
||||
final tokenContract = widget.isToken
|
||||
? ref
|
||||
.watch(tokenServiceProvider.select((value) => value!.tokenContract))
|
||||
? ref.watch(pCurrentTokenWallet.select((value) => value!.tokenContract))
|
||||
: null;
|
||||
|
||||
final priceTuple = widget.isToken
|
||||
|
@ -104,7 +104,8 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
|
|||
}
|
||||
} else {
|
||||
Balance balance = widget.isToken
|
||||
? ref.watch(tokenServiceProvider.select((value) => value!.balance))
|
||||
? ref.watch(pTokenBalance(
|
||||
(walletId: walletId, contractAddress: tokenContract!.address)))
|
||||
: ref.watch(pWalletBalance(walletId));
|
||||
|
||||
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()}");
|
||||
|
||||
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) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
|
|
|
@ -1,611 +1,152 @@
|
|||
/*
|
||||
* 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 '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")) {
|
||||
// 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');
|
||||
|
||||
_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());
|
||||
}
|
||||
}
|
||||
// /*
|
||||
// * 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 '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 {
|
||||
// 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),
|
||||
// ],
|
||||
// ethWallet.walletId,
|
||||
// );
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// 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<List<Transaction>> get transactions => ethWallet.mainDB
|
||||
// .getTransactions(ethWallet.walletId)
|
||||
// .filter()
|
||||
// .otherDataEqualTo(tokenContract.address)
|
||||
// .sortByTimestampDesc()
|
||||
// .findAll();
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// }
|
||||
|
|
|
@ -12,7 +12,6 @@ import 'package:bip32/bip32.dart' as bip32;
|
|||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:decimal/decimal.dart';
|
||||
import "package:hex/hex.dart";
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/constants.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>);
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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));
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:ethereum_addresses/ethereum_addresses.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
@ -56,6 +57,24 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
|||
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 ==============================================
|
||||
|
||||
Future<void> _initCredentials(
|
||||
|
@ -118,7 +137,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
|||
|
||||
@override
|
||||
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
||||
return estimateFee(
|
||||
return estimateEthFee(
|
||||
feeRate,
|
||||
(cryptoCurrency as Ethereum).gasLimit,
|
||||
cryptoCurrency.fractionDigits,
|
||||
|
@ -249,7 +268,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
|||
|
||||
//Calculate fees (GasLimit * gasPrice)
|
||||
// int txFee = element.gasPrice * element.gasUsed;
|
||||
Amount txFee = element.gasCost;
|
||||
final Amount txFee = element.gasCost;
|
||||
final transactionAmount = element.value;
|
||||
final addressFrom = checksumEthereumAddress(element.from);
|
||||
final addressTo = checksumEthereumAddress(element.to);
|
||||
|
@ -267,7 +286,7 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
|||
continue;
|
||||
}
|
||||
|
||||
// hack epic tx data into inputs and outputs
|
||||
// hack eth tx data into inputs and outputs
|
||||
final List<OutputV2> outputs = [];
|
||||
final List<InputV2> inputs = [];
|
||||
|
||||
|
|
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();
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/models.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/providers/global/wallets_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/fee_rate_type_enum.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/widgets/animated_text.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
|
@ -168,8 +168,8 @@ class _DesktopFeeDialogState extends ConsumerState<DesktopFeeDialog> {
|
|||
await wallet.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
final tokenWallet = ref.read(pCurrentTokenWallet)!;
|
||||
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
|
||||
ref.read(tokenFeeSessionCacheProvider).slow[amount] = fee;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_wallet_view.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/services/ethereum/ethereum_token_service.dart';
|
||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/show_loading.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/sub_wallets/eth_token_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
|
@ -55,17 +54,16 @@ class SimpleWalletCard extends ConsumerWidget {
|
|||
Wallet wallet,
|
||||
EthContract contract,
|
||||
) async {
|
||||
final old = ref.read(tokenServiceStateProvider);
|
||||
// exit previous if there is one
|
||||
unawaited(old?.exit());
|
||||
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
|
||||
token: contract,
|
||||
secureStore: ref.read(secureStoreProvider),
|
||||
ethWallet: wallet as EthereumWallet,
|
||||
tracker: TransactionNotificationTracker(
|
||||
walletId: walletId,
|
||||
),
|
||||
wallet as EthereumWallet,
|
||||
contract,
|
||||
);
|
||||
|
||||
try {
|
||||
await ref.read(tokenServiceProvider)!.initialize();
|
||||
await ref.read(pCurrentTokenWallet)!.init();
|
||||
return true;
|
||||
} catch (_) {
|
||||
await showDialog<void>(
|
||||
|
|
Loading…
Reference in a new issue