do not use secure storage for token ABIs

This commit is contained in:
julian 2023-03-03 08:35:43 -06:00
parent babbd75da3
commit f26fb19453
5 changed files with 77 additions and 67 deletions

View file

@ -366,8 +366,8 @@ class MainDB {
QueryBuilder<EthContract, EthContract, QWhere> getEthContracts() =>
isar.ethContracts.where();
Future<void> putEthContract(EthContract contract) => isar.writeTxn(() async {
await isar.ethContracts.put(contract);
Future<int> putEthContract(EthContract contract) => isar.writeTxn(() async {
return await isar.ethContracts.put(contract);
});
Future<void> putEthContracts(List<EthContract> contracts) =>

View file

@ -20,7 +20,7 @@ class TokenSummary extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final token =
ref.watch(tokenServiceProvider.select((value) => value!.token));
ref.watch(tokenServiceProvider.select((value) => value!.tokenContract));
final balance =
ref.watch(tokenServiceProvider.select((value) => value!.balance));

View file

@ -78,7 +78,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
SvgPicture.asset(
Assets.svg.iconForToken(
contractAddress: ref.watch(tokenServiceProvider
.select((value) => value!.token.address))),
.select((value) => value!.tokenContract.address))),
width: 24,
height: 24,
),
@ -86,8 +86,8 @@ class _TokenViewState extends ConsumerState<TokenView> {
width: 10,
),
Text(
ref.watch(
tokenServiceProvider.select((value) => value!.token.name)),
ref.watch(tokenServiceProvider
.select((value) => value!.tokenContract.name)),
style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis,
),
@ -118,7 +118,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
walletId: widget.walletId,
initialSyncStatus: initialSyncStatus,
tokenContractAddress: ref.watch(tokenServiceProvider
.select((value) => value!.token.address)),
.select((value) => value!.tokenContract.address)),
),
),
),

View file

@ -425,6 +425,7 @@ abstract class EthereumAPI {
}
}
/// Fetch the underlying contract address that a proxy contract points to
static Future<EthereumResponse<String>> getProxyTokenImplementation(
String contractAddress) async {
try {

View file

@ -5,6 +5,7 @@ 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/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
@ -28,31 +29,33 @@ import 'package:tuple/tuple.dart';
import 'package:web3dart/web3dart.dart' as web3dart;
class EthereumTokenService extends ChangeNotifier with EthTokenCache {
final EthContract token;
final EthereumWallet ethWallet;
final TransactionNotificationTracker tracker;
final SecureStorageInterface _secureStore;
late web3dart.EthereumAddress _contractAddress;
// late web3dart.EthereumAddress _contractAddress;
late web3dart.EthPrivateKey _credentials;
late web3dart.DeployedContract _contract;
late web3dart.DeployedContract _deployedContract;
late web3dart.ContractFunction _balanceFunction;
late web3dart.ContractFunction _sendFunction;
late String _tokenAbi;
late web3dart.Web3Client _client;
static const _gasLimit = 200000;
EthereumTokenService({
required this.token,
required EthContract token,
required this.ethWallet,
required SecureStorageInterface secureStore,
required this.tracker,
}) : _secureStore = secureStore {
_contractAddress = web3dart.EthereumAddress.fromHex(token.address);
}) : _secureStore = secureStore,
_tokenContract = token {
// _contractAddress = web3dart.EthereumAddress.fromHex(token.address);
initCache(ethWallet.walletId, token);
}
EthContract get tokenContract => _tokenContract;
EthContract _tokenContract;
TokenBalance get balance => _balance ??= getCachedBalance();
TokenBalance? _balance;
@ -63,12 +66,12 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
final decimalAmount =
Format.satoshisToAmount(amount as int, coin: Coin.ethereum);
final bigIntAmount =
amountToBigInt(decimalAmount.toDouble(), token.decimals);
amountToBigInt(decimalAmount.toDouble(), tokenContract.decimals);
final sentTx = await _client.sendTransaction(
_credentials,
web3dart.Transaction.callContract(
contract: _contract,
contract: _deployedContract,
function: _sendFunction,
parameters: [
web3dart.EthereumAddress.fromHex(txData['address'] as String),
@ -96,7 +99,7 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
.findFirst();
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
final fee = estimateFee(feeRate, _gasLimit, token.decimals);
final fee = estimateFee(feeRate, _gasLimit, tokenContract.decimals);
return Format.decimalAmountToSatoshis(Decimal.parse(fee.toString()), coin);
}
@ -107,69 +110,75 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
return await EthereumAPI.getFees();
}
Future<void> initialize() async {
final storedABI =
await _secureStore.read(key: '${_contractAddress.toString()}_tokenAbi');
if (storedABI == null) {
final abiResponse = await EthereumAPI.getTokenAbi(_contractAddress.hex);
//Fetch token ABI so we can call token functions
if (abiResponse.value != null) {
_tokenAbi = abiResponse.value!;
//Store abi in secure store
await _secureStore.write(
key: '${_contractAddress.hex}_tokenAbi',
value: _tokenAbi,
);
} else {
throw abiResponse.exception!;
}
Future<EthContract> _updateTokenABI({
required EthContract forContract,
required String usingContractAddress,
}) async {
final abiResponse = await EthereumAPI.getTokenAbi(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 {
_tokenAbi = storedABI;
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.mnemonicString;
//Get private key for given mnemonic
String privateKey = getPrivateKey(
mnemonicString!, (await ethWallet.mnemonicPassphrase) ?? "");
mnemonicString!,
(await ethWallet.mnemonicPassphrase) ?? "",
);
_credentials = web3dart.EthPrivateKey.fromHex(privateKey);
_contract = web3dart.DeployedContract(
web3dart.ContractAbi.fromJson(_tokenAbi, token.name), _contractAddress);
_deployedContract = web3dart.DeployedContract(
web3dart.ContractAbi.fromJson(tokenContract.abi!, tokenContract.name),
contractAddress,
);
try {
_balanceFunction = _contract.function('balanceOf');
_sendFunction = _contract.function('transfer');
_balanceFunction = _deployedContract.function('balanceOf');
_sendFunction = _deployedContract.function('transfer');
} catch (_) {
// function not found so likely a proxy so we need to fetch the impl
final response =
await EthereumAPI.getProxyTokenImplementation(_contractAddress.hex);
final contractAddressResponse =
await EthereumAPI.getProxyTokenImplementation(contractAddress.hex);
if (response.value != null) {
final abiResponse = await EthereumAPI.getTokenAbi(response.value!);
if (abiResponse.value != null) {
_tokenAbi = abiResponse.value!;
await _secureStore.write(
key: '${_contractAddress.hex}_tokenAbi', value: _tokenAbi);
} else {
throw abiResponse.exception!;
}
if (contractAddressResponse.value != null) {
_tokenContract = await _updateTokenABI(
forContract: tokenContract,
usingContractAddress: contractAddressResponse.value!,
);
} else {
throw response.exception!;
throw contractAddressResponse.exception!;
}
}
_contract = web3dart.DeployedContract(
_deployedContract = web3dart.DeployedContract(
web3dart.ContractAbi.fromJson(
_tokenAbi,
token.name,
tokenContract.abi!,
tokenContract.name,
),
_contractAddress,
contractAddress,
);
_balanceFunction = _contract.function('balanceOf');
_sendFunction = _contract.function('transfer');
_balanceFunction = _deployedContract.function('balanceOf');
_sendFunction = _deployedContract.function('transfer');
_client = await getEthClient();
@ -218,7 +227,7 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.syncing,
ethWallet.walletId + token.address,
ethWallet.walletId + tokenContract.address,
coin,
),
);
@ -227,7 +236,7 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
await _refreshTransactions();
} catch (e, s) {
Logging.instance.log(
"Caught exception in ${token.name} ${ethWallet.walletName} ${ethWallet.walletId} refresh(): $e\n$s",
"Caught exception in ${tokenContract.name} ${ethWallet.walletName} ${ethWallet.walletId} refresh(): $e\n$s",
level: LogLevel.Warning,
);
} finally {
@ -235,7 +244,7 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.synced,
ethWallet.walletId + token.address,
ethWallet.walletId + tokenContract.address,
coin,
),
);
@ -246,7 +255,7 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
Future<void> refreshCachedBalance() async {
final balanceRequest = await _client.call(
contract: _contract,
contract: _deployedContract,
function: _balanceFunction,
params: [_credentials.address],
);
@ -254,12 +263,12 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
String _balance = balanceRequest.first.toString();
final newBalance = TokenBalance(
contractAddress: token.address,
contractAddress: tokenContract.address,
total: int.parse(_balance),
spendable: int.parse(_balance),
blockedTotal: 0,
pendingSpendable: 0,
decimalPlaces: token.decimals,
decimalPlaces: tokenContract.decimals,
);
await updateCachedBalance(newBalance);
notifyListeners();
@ -268,7 +277,7 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
Future<List<Transaction>> get transactions => ethWallet.db
.getTransactions(ethWallet.walletId)
.filter()
.otherDataEqualTo(token.address)
.otherDataEqualTo(tokenContract.address)
.sortByTimestampDesc()
.findAll();
@ -277,7 +286,7 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
final response = await EthereumAPI.getTokenTransactions(
address: addressString,
contractAddress: token.address,
contractAddress: tokenContract.address,
);
if (response.value == null) {
@ -358,7 +367,7 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
if (txnsData.isNotEmpty) {
GlobalEventBus.instance.fire(
UpdatedInBackgroundEvent(
"${token.name} transactions updated/added for: ${ethWallet.walletId} ${ethWallet.walletName}",
"${tokenContract.name} transactions updated/added for: ${ethWallet.walletId} ${ethWallet.walletName}",
ethWallet.walletId,
),
);