WIP eth token abi fetch and parsing

This commit is contained in:
julian 2023-03-23 18:05:35 -06:00
parent 85c416fb50
commit b27b90c08a
2 changed files with 161 additions and 61 deletions

View file

@ -468,46 +468,89 @@ abstract class EthereumAPI {
} }
} }
/// Fetch the underlying contract address that a proxy contract points to // static Future<EthereumResponse<String>> getTokenAbi22(
static Future<EthereumResponse<String>> getProxyTokenImplementation( // String contractAddress) async {
String contractAddress) async { // try {
try { // final response = await get(
final response = await get(Uri.parse( // Uri.parse(
// "$stackURI?module=contract&action=getsourcecode&address=$contractAddress")); // "https://api.etherscan.io/api?module=contract&action=getabi&address=$contractAddress&apikey=EG6J7RJIQVSTP2BS59D3TY2G55YHS5F2HP",
"$etherscanApi?module=contract&action=getsourcecode&address=$contractAddress&apikey=EG6J7RJIQVSTP2BS59D3TY2G55YHS5F2HP")); // ),
if (response.statusCode == 200) { // );
final json = jsonDecode(response.body); //
if (json["message"] == "OK") { // if (response.statusCode == 200) {
final list = json["result"] as List; // final json = jsonDecode(response.body) as Map;
final map = Map<String, dynamic>.from(list.first as Map); // print("========================== 222222222222222 ================");
// dev.log((jsonDecode(json["result"] as String)).toString());
// print(
// "============================ 2222222222222222222 ==============");
//
// return EthereumResponse(
// json["result"] as String,
// null,
// );
// } else {
// throw EthApiException(
// "getTokenAbi($contractAddress) failed with status code: "
// "${response.statusCode}",
// );
// }
// } on EthApiException catch (e) {
// return EthereumResponse(
// null,
// e,
// );
// } catch (e, s) {
// Logging.instance.log(
// "getTokenAbi(): $e\n$s",
// level: LogLevel.Error,
// );
// return EthereumResponse(
// null,
// EthApiException(e.toString()),
// );
// }
// }
return EthereumResponse( // /// Fetch the underlying contract address that a proxy contract points to
map["Implementation"] as String, // static Future<EthereumResponse<String>> getProxyTokenImplementation(
null, // String contractAddress) async {
); // try {
} else { // final response = await get(Uri.parse(
throw EthApiException(json["message"] as String); // "$stackURI?module=contract&action=getsourcecode&address=$contractAddress"));
} // // "$etherscanApi?module=contract&action=getsourcecode&address=$contractAddress&apikey=EG6J7RJIQVSTP2BS59D3TY2G55YHS5F2HP"));
} else { // if (response.statusCode == 200) {
throw EthApiException( // final json = jsonDecode(response.body);
"fetchProxyTokenImplementation($contractAddress) failed with status code: " // if (json["message"] == "OK") {
"${response.statusCode}", // final list = json["result"] as List;
); // final map = Map<String, dynamic>.from(list.first as Map);
} //
} on EthApiException catch (e) { // return EthereumResponse(
return EthereumResponse( // map["Implementation"] as String,
null, // null,
e, // );
); // } else {
} catch (e, s) { // throw EthApiException(json["message"] as String);
Logging.instance.log( // }
"fetchProxyTokenImplementation(): $e\n$s", // } else {
level: LogLevel.Error, // throw EthApiException(
); // "fetchProxyTokenImplementation($contractAddress) failed with status code: "
return EthereumResponse( // "${response.statusCode}",
null, // );
EthApiException(e.toString()), // }
); // } on EthApiException catch (e) {
} // return EthereumResponse(
} // null,
// e,
// );
// } catch (e, s) {
// Logging.instance.log(
// "fetchProxyTokenImplementation(): $e\n$s",
// level: LogLevel.Error,
// );
// return EthereumResponse(
// null,
// EthApiException(e.toString()),
// );
// }
// }
} }

View file

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:ethereum_addresses/ethereum_addresses.dart'; import 'package:ethereum_addresses/ethereum_addresses.dart';
@ -24,6 +25,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/impl/contract_abi.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';
@ -116,7 +118,10 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
required EthContract forContract, required EthContract forContract,
required String usingContractAddress, required String usingContractAddress,
}) async { }) async {
final abiResponse = await EthereumAPI.getTokenAbi(usingContractAddress); final abiResponse = await EthereumAPI.getTokenAbi(
name: forContract.name,
contractAddress: usingContractAddress,
);
// Fetch token ABI so we can call token functions // Fetch token ABI so we can call token functions
if (abiResponse.value != null) { if (abiResponse.value != null) {
final updatedToken = forContract.copyWith(abi: abiResponse.value!); final updatedToken = forContract.copyWith(abi: abiResponse.value!);
@ -132,12 +137,12 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
final contractAddress = final contractAddress =
web3dart.EthereumAddress.fromHex(tokenContract.address); web3dart.EthereumAddress.fromHex(tokenContract.address);
if (tokenContract.abi == null) { // if (tokenContract.abi == null) {
_tokenContract = await _updateTokenABI( _tokenContract = await _updateTokenABI(
forContract: tokenContract, forContract: tokenContract,
usingContractAddress: contractAddress.hex, usingContractAddress: contractAddress.hex,
); );
} // }
String? mnemonicString = await ethWallet.mnemonicString; String? mnemonicString = await ethWallet.mnemonicString;
@ -149,7 +154,10 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
_credentials = web3dart.EthPrivateKey.fromHex(privateKey); _credentials = web3dart.EthPrivateKey.fromHex(privateKey);
_deployedContract = web3dart.DeployedContract( _deployedContract = web3dart.DeployedContract(
web3dart.ContractAbi.fromJson(tokenContract.abi!, tokenContract.name), ContractAbiExtensions.fromJsonList(
jsonList: tokenContract.abi!,
name: tokenContract.name,
),
contractAddress, contractAddress,
); );
@ -157,24 +165,73 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
_balanceFunction = _deployedContract.function('balanceOf'); _balanceFunction = _deployedContract.function('balanceOf');
_sendFunction = _deployedContract.function('transfer'); _sendFunction = _deployedContract.function('transfer');
} catch (_) { } catch (_) {
// function not found so likely a proxy so we need to fetch the impl //====================================================================
final contractAddressResponse = final list = List<Map<String, dynamic>>.from(
await EthereumAPI.getProxyTokenImplementation(contractAddress.hex); jsonDecode(tokenContract.abi!) as List);
final functionNames = list.map((e) => e["name"] as String);
if (contractAddressResponse.value != null) { if (!functionNames.contains("balanceOf")) {
_tokenContract = await _updateTokenABI( list.add(
forContract: tokenContract, {
usingContractAddress: contractAddressResponse.value!, "encoding": "0x70a08231",
"inputs": [
{"name": "account", "type": "address"}
],
"name": "balanceOf",
"outputs": [
{"name": "val_0", "type": "uint256"}
],
"signature": "balanceOf(address)",
"type": "function"
},
); );
} else {
throw contractAddressResponse.exception!;
} }
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.getProxyTokenImplementation(contractAddress.hex);
//
// if (contractAddressResponse.value != null) {
// _tokenContract = await _updateTokenABI(
// forContract: tokenContract,
// usingContractAddress: contractAddressResponse.value!,
// );
// } else {
// throw contractAddressResponse.exception!;
// }
//====================================================================
} }
_deployedContract = web3dart.DeployedContract( _deployedContract = web3dart.DeployedContract(
web3dart.ContractAbi.fromJson( ContractAbiExtensions.fromJsonList(
tokenContract.abi!, jsonList: tokenContract.abi!,
tokenContract.name, name: tokenContract.name,
), ),
contractAddress, contractAddress,
); );