eth + token send fixes

This commit is contained in:
julian 2023-03-31 17:17:15 -06:00
parent 1de0eec6cc
commit 6704d82889
2 changed files with 212 additions and 44 deletions

View file

@ -32,6 +32,7 @@ import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/eth_commons.dart'; import 'package:stackwallet/utilities/eth_commons.dart';
import 'package:stackwallet/utilities/extensions/extensions.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
@ -183,7 +184,8 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
Future<List<Transaction>> get transactions => db Future<List<Transaction>> get transactions => db
.getTransactions(walletId) .getTransactions(walletId)
.filter() .filter()
.otherDataEqualTo(null) .otherDataEqualTo(
null) // eth txns with other data where other data is the token contract address
.sortByTimestampDesc() .sortByTimestampDesc()
.findAll(); .findAll();
@ -440,22 +442,70 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
final feeEstimate = await estimateFeeFor(satoshiAmount, fee); final feeEstimate = await estimateFeeFor(satoshiAmount, fee);
bool isSendAll = false; // bool isSendAll = false;
final availableBalance = balance.spendable; // final availableBalance = balance.spendable;
if (satoshiAmount == availableBalance) { // if (satoshiAmount == availableBalance) {
isSendAll = true; // isSendAll = true;
} // }
//
// if (isSendAll) {
// //Subtract fee amount from send amount
// satoshiAmount -= feeEstimate;
// }
if (isSendAll) { final decimalAmount = Format.satoshisToAmount(satoshiAmount, coin: coin);
//Subtract fee amount from send amount final bigIntAmount = amountToBigInt(
satoshiAmount -= feeEstimate; decimalAmount.toDouble(),
} Constants.decimalPlacesForCoin(coin),
);
final client = getEthClient();
final myAddress = await currentReceivingAddress;
final myWeb3Address = web3.EthereumAddress.fromHex(myAddress);
final est = await client.estimateGas(
sender: myWeb3Address,
to: web3.EthereumAddress.fromHex(address),
gasPrice: web3.EtherAmount.fromUnitAndValue(
web3.EtherUnit.wei,
fee,
),
amountOfGas: BigInt.from(_gasLimit),
value: web3.EtherAmount.inWei(bigIntAmount),
);
final nonce = args?["nonce"] as int? ??
await client.getTransactionCount(myWeb3Address,
atBlock: const web3.BlockNum.pending());
final nResponse = await EthereumAPI.getAddressNonce(address: myAddress);
print("==============================================================");
print("ETH client.estimateGas: $est");
print("ETH estimateFeeFor : $feeEstimate");
print("ETH nonce custom response: $nResponse");
print("ETH actual nonce : $nonce");
print("==============================================================");
final tx = web3.Transaction(
to: web3.EthereumAddress.fromHex(address),
gasPrice: web3.EtherAmount.fromUnitAndValue(
web3.EtherUnit.wei,
fee,
),
maxGas: _gasLimit,
value: web3.EtherAmount.inWei(bigIntAmount),
nonce: nonce,
);
Map<String, dynamic> txData = { Map<String, dynamic> txData = {
"fee": feeEstimate, "fee": feeEstimate,
"feeInWei": fee, "feeInWei": fee,
"address": address, "address": address,
"recipientAmt": satoshiAmount, "recipientAmt": satoshiAmount,
"ethTx": tx,
"chainId": (await client.getChainId()).toInt(),
"nonce": tx.nonce,
}; };
return txData; return txData;
@ -464,22 +514,12 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
@override @override
Future<String> confirmSend({required Map<String, dynamic> txData}) async { Future<String> confirmSend({required Map<String, dynamic> txData}) async {
web3.Web3Client client = getEthClient(); web3.Web3Client client = getEthClient();
final chainId = await client.getChainId();
final amount = txData['recipientAmt'] as int;
final decimalAmount = Format.satoshisToAmount(amount, coin: coin);
final bigIntAmount = amountToBigInt(
decimalAmount.toDouble(),
Constants.decimalPlacesForCoin(coin),
);
final tx = web3.Transaction( final txid = await client.sendTransaction(
to: web3.EthereumAddress.fromHex(txData['address'] as String), _credentials,
gasPrice: web3.EtherAmount.fromUnitAndValue( txData["ethTx"] as web3.Transaction,
web3.EtherUnit.wei, txData['feeInWei']), chainId: txData["chainId"] as int,
maxGas: _gasLimit, );
value: web3.EtherAmount.inWei(bigIntAmount));
final txid = await client.sendTransaction(_credentials, tx,
chainId: chainId.toInt());
return txid; return txid;
} }
@ -558,7 +598,6 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
.findAll(); .findAll();
Future<bool> refreshIfThereIsNewData() async { Future<bool> refreshIfThereIsNewData() async {
web3.Web3Client client = getEthClient();
if (longMutex) return false; if (longMutex) return false;
if (_hasCalledExit) return false; if (_hasCalledExit) return false;
final currentChainHeight = await chainHeight; final currentChainHeight = await chainHeight;
@ -574,9 +613,10 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
} }
for (String txid in txnsToCheck) { for (String txid in txnsToCheck) {
final txn = await client.getTransactionByHash(txid); final response = await EthereumAPI.getEthTransactionByHash(txid);
final int txBlockNumber = txn.blockNumber.blockNum; final txBlockNumber = response.value?.blockNumber;
if (txBlockNumber != null) {
final int txConfirmations = currentChainHeight - txBlockNumber; final int txConfirmations = currentChainHeight - txBlockNumber;
bool isUnconfirmed = txConfirmations < MINIMUM_CONFIRMATIONS; bool isUnconfirmed = txConfirmations < MINIMUM_CONFIRMATIONS;
if (!isUnconfirmed) { if (!isUnconfirmed) {
@ -584,6 +624,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
break; break;
} }
} }
}
if (!needsRefresh) { if (!needsRefresh) {
var allOwnAddresses = await _fetchAllOwnAddresses(); var allOwnAddresses = await _fetchAllOwnAddresses();
final response = await EthereumAPI.getEthTransactions( final response = await EthereumAPI.getEthTransactions(
@ -857,7 +898,54 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
@override @override
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async { Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async {
//Only used for Electrumx coins final txid = txData["txid"] as String;
final addressString = txData["address"] as String;
final response = await EthereumAPI.getEthTransactionByHash(txid);
final transaction = Transaction(
walletId: walletId,
txid: txid,
timestamp: DateTime.now().millisecondsSinceEpoch ~/ 1000,
type: TransactionType.outgoing,
subType: TransactionSubType.none,
amount: txData["recipientAmt"] as int,
amountString: Amount(
rawValue: BigInt.from(txData["recipientAmt"] as int),
fractionDigits: coin.decimals,
).toJsonString(),
fee: txData["fee"] as int,
height: null,
isCancelled: false,
isLelantus: false,
otherData: null,
slateId: null,
nonce: (txData["nonce"] as int?) ??
response.value?.nonce.toBigIntFromHex.toInt(),
inputs: [],
outputs: [],
);
Address? address = await db.getAddress(
walletId,
addressString,
);
address ??= Address(
walletId: walletId,
value: addressString,
publicKey: [],
derivationIndex: -1,
derivationPath: null,
type: AddressType.ethereum,
subType: AddressSubType.nonWallet,
);
await db.addNewTransactionData(
[
Tuple2(transaction, address),
],
walletId,
);
} }
@override @override

View file

@ -95,8 +95,11 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
final client = await getEthClient(); final client = await getEthClient();
final myAddress = await currentReceivingAddress;
final myWeb3Address = web3dart.EthereumAddress.fromHex(myAddress);
final est = await client.estimateGas( final est = await client.estimateGas(
sender: web3dart.EthereumAddress.fromHex(await currentReceivingAddress), sender: myWeb3Address,
to: web3dart.EthereumAddress.fromHex(address), to: web3dart.EthereumAddress.fromHex(address),
data: _sendFunction.encodeCall( data: _sendFunction.encodeCall(
[web3dart.EthereumAddress.fromHex(address), bigIntAmount]), [web3dart.EthereumAddress.fromHex(address), bigIntAmount]),
@ -108,9 +111,16 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
value: web3dart.EtherAmount.inWei(BigInt.one), value: web3dart.EtherAmount.inWei(BigInt.one),
); );
final nonce = args?["nonce"] as int? ??
await client.getTransactionCount(myWeb3Address,
atBlock: const web3dart.BlockNum.pending());
final nResponse = await EthereumAPI.getAddressNonce(address: myAddress);
print("=============================================================="); print("==============================================================");
print("client.estimateGas: $est"); print("TOKEN client.estimateGas: $est");
print("estimateFeeFor : $feeEstimate"); print("TOKEN estimateFeeFor : $feeEstimate");
print("TOKEN nonce custom response: $nResponse");
print("TOKEN actual nonce : $nonce");
print("=============================================================="); print("==============================================================");
final tx = web3dart.Transaction.callContract( final tx = web3dart.Transaction.callContract(
@ -122,7 +132,7 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
web3dart.EtherUnit.wei, web3dart.EtherUnit.wei,
fee, fee,
), ),
nonce: args?["nonce"] as int?, nonce: nonce,
); );
Map<String, dynamic> txData = { Map<String, dynamic> txData = {
@ -131,18 +141,88 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
"address": address, "address": address,
"recipientAmt": satoshiAmount, "recipientAmt": satoshiAmount,
"ethTx": tx, "ethTx": tx,
"chainId": (await client.getChainId()).toInt(),
"nonce": tx.nonce,
}; };
return txData; return txData;
} }
Future<String> confirmSend({required Map<String, dynamic> txData}) async { Future<String> confirmSend({required Map<String, dynamic> txData}) async {
try {
final txid = await _client.sendTransaction( final txid = await _client.sendTransaction(
_credentials, _credentials,
txData["ethTx"] as web3dart.Transaction, 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; 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 = 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,
amount: txData["recipientAmt"] as int,
amountString: Amount(
rawValue: BigInt.from(txData["recipientAmt"] as int),
fractionDigits: tokenContract.decimals,
).toJsonString(),
fee: txData["fee"] as int,
height: null,
isCancelled: false,
isLelantus: false,
otherData: tokenContract.address,
slateId: null,
nonce: (txData["nonce"] as int?) ??
response.value?.nonce.toBigIntFromHex.toInt(),
inputs: [],
outputs: [],
);
Address? address = await ethWallet.db.getAddress(
ethWallet.walletId,
addressString,
);
address ??= Address(
walletId: ethWallet.walletId,
value: addressString,
publicKey: [],
derivationIndex: -1,
derivationPath: null,
type: AddressType.ethereum,
subType: AddressSubType.nonWallet,
);
await ethWallet.db.addNewTransactionData(
[
Tuple2(transaction, address),
],
ethWallet.walletId,
);
} }
Future<String> get currentReceivingAddress async { Future<String> get currentReceivingAddress async {
@ -499,14 +579,14 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
return isValidEthereumAddress(address); return isValidEthereumAddress(address);
} }
Future<NodeModel> getCurrentNode() async { NodeModel getCurrentNode() {
return NodeService(secureStorageInterface: _secureStore) return NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ?? .getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin); DefaultNodes.getNodeFor(coin);
} }
Future<web3dart.Web3Client> getEthClient() async { Future<web3dart.Web3Client> getEthClient() async {
final node = await getCurrentNode(); final node = getCurrentNode();
return web3dart.Web3Client(node.host, Client()); return web3dart.Web3Client(node.host, Client());
} }
} }