mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-24 03:15:50 +00:00
eth + token send fixes
This commit is contained in:
parent
1de0eec6cc
commit
6704d82889
2 changed files with 212 additions and 44 deletions
|
@ -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,14 +613,16 @@ 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;
|
||||||
|
|
||||||
final int txConfirmations = currentChainHeight - txBlockNumber;
|
if (txBlockNumber != null) {
|
||||||
bool isUnconfirmed = txConfirmations < MINIMUM_CONFIRMATIONS;
|
final int txConfirmations = currentChainHeight - txBlockNumber;
|
||||||
if (!isUnconfirmed) {
|
bool isUnconfirmed = txConfirmations < MINIMUM_CONFIRMATIONS;
|
||||||
needsRefresh = true;
|
if (!isUnconfirmed) {
|
||||||
break;
|
needsRefresh = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!needsRefresh) {
|
if (!needsRefresh) {
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
final txid = await _client.sendTransaction(
|
try {
|
||||||
_credentials,
|
final txid = await _client.sendTransaction(
|
||||||
txData["ethTx"] as web3dart.Transaction,
|
_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 = 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: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
return txid;
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue