get token abi fixes

This commit is contained in:
julian 2023-03-01 18:02:53 -06:00
parent 16efeea1db
commit 8466180b47
2 changed files with 112 additions and 89 deletions

View file

@ -9,26 +9,6 @@ import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/utilities/eth_commons.dart';
import 'package:stackwallet/utilities/logger.dart';
class AbiRequestResponse {
final String message;
final String result;
final String status;
const AbiRequestResponse({
required this.message,
required this.result,
required this.status,
});
factory AbiRequestResponse.fromJson(Map<String, dynamic> json) {
return AbiRequestResponse(
message: json['message'] as String,
result: json['result'] as String,
status: json['status'] as String,
);
}
}
class EthTokenTx {
final String blockHash;
final int blockNumber;
@ -191,7 +171,7 @@ abstract class EthereumAPI {
}
} else {
throw EthApiException(
"getWalletTokens($address) failed with status code: "
"getTokenTransactions($address) failed with status code: "
"${response.statusCode}",
);
}
@ -202,7 +182,7 @@ abstract class EthereumAPI {
);
} catch (e, s) {
Logging.instance.log(
"getWalletTokens(): $e\n$s",
"getTokenTransactions(): $e\n$s",
level: LogLevel.Error,
);
return EthereumResponse(
@ -310,7 +290,7 @@ abstract class EthereumAPI {
}
} else {
throw EthApiException(
"getWalletTokens($address) failed with status code: "
"getWalletTokenBalance($address) failed with status code: "
"${response.statusCode}",
);
}
@ -321,7 +301,7 @@ abstract class EthereumAPI {
);
} catch (e, s) {
Logging.instance.log(
"getWalletTokens(): $e\n$s",
"getWalletTokenBalance(): $e\n$s",
level: LogLevel.Error,
);
return EthereumResponse(
@ -356,7 +336,6 @@ abstract class EthereumAPI {
slow: feesSlow.toInt());
}
//Validate that a custom token is valid and is ERC-20, a token will be valid
static Future<EthereumResponse<EthContractInfo>> getTokenByContractAddress(
String contractAddress) async {
try {
@ -407,7 +386,7 @@ abstract class EthereumAPI {
);
} catch (e, s) {
Logging.instance.log(
"getWalletTokens(): $e\n$s",
"getTokenByContractAddress(): $e\n$s",
level: LogLevel.Error,
);
return EthereumResponse(
@ -417,15 +396,82 @@ abstract class EthereumAPI {
}
}
static Future<AbiRequestResponse> fetchTokenAbi(
static Future<EthereumResponse<String>> getTokenAbi(
String contractAddress) async {
final response = await get(Uri.parse(
"$etherscanApi?module=contract&action=getabi&address=$contractAddress&apikey=EG6J7RJIQVSTP2BS59D3TY2G55YHS5F2HP"));
if (response.statusCode == 200) {
return AbiRequestResponse.fromJson(
json.decode(response.body) as Map<String, dynamic>);
} else {
throw Exception("ERROR GETTING TOKENABI ${response.reasonPhrase}");
try {
final response = await get(Uri.parse(
"$etherscanApi?module=contract&action=getabi&address=$contractAddress&apikey=EG6J7RJIQVSTP2BS59D3TY2G55YHS5F2HP"));
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
if (json["message"] == "OK") {
return EthereumResponse(
json["result"] as String,
null,
);
} else {
throw EthApiException(json["message"] as String);
}
} 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()),
);
}
}
static Future<EthereumResponse<String>> getProxyTokenImplementation(
String contractAddress) async {
try {
final response = await get(Uri.parse(
"$etherscanApi?module=contract&action=getsourcecode&address=$contractAddress&apikey=EG6J7RJIQVSTP2BS59D3TY2G55YHS5F2HP"));
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
if (json["message"] == "OK") {
final list = json["result"] as List;
final map = Map<String, dynamic>.from(list.first as Map);
return EthereumResponse(
map["Implementation"] as String,
null,
);
} else {
throw EthApiException(json["message"] as String);
}
} else {
throw EthApiException(
"fetchProxyTokenImplementation($contractAddress) failed with status code: "
"${response.statusCode}",
);
}
} 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,5 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'package:decimal/decimal.dart';
import 'package:ethereum_addresses/ethereum_addresses.dart';
@ -115,16 +114,17 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
await _secureStore.read(key: '${_contractAddress.toString()}_tokenAbi');
if (storedABI == null) {
AbiRequestResponse abi =
await EthereumAPI.fetchTokenAbi(_contractAddress.hex);
final abiResponse = await EthereumAPI.getTokenAbi(_contractAddress.hex);
//Fetch token ABI so we can call token functions
if (abi.message == "OK") {
_tokenAbi = abi.result;
if (abiResponse.value != null) {
_tokenAbi = abiResponse.value!;
//Store abi in secure store
await _secureStore.write(
key: '${_contractAddress.hex}_tokenAbi', value: _tokenAbi);
key: '${_contractAddress.hex}_tokenAbi',
value: _tokenAbi,
);
} else {
throw Exception('Failed to load token abi');
throw abiResponse.exception!;
}
} else {
_tokenAbi = storedABI;
@ -140,61 +140,38 @@ class EthereumTokenService extends ChangeNotifier with EthTokenCache {
_contract = web3dart.DeployedContract(
web3dart.ContractAbi.fromJson(_tokenAbi, token.name), _contractAddress);
bool hackInBalanceOf = false, hackInTransfer = false;
try {
_balanceFunction = _contract.function('balanceOf');
} catch (_) {
// function not found so likely a proxy so we need to hack the function in
hackInBalanceOf = true;
}
try {
_sendFunction = _contract.function('transfer');
} catch (_) {
// function not found so likely a proxy so we need to hack the function in
hackInTransfer = true;
// function not found so likely a proxy so we need to fetch the impl
final response =
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!;
}
} else {
throw response.exception!;
}
}
if (hackInBalanceOf || hackInTransfer) {
final json = jsonDecode(_tokenAbi) as List;
if (hackInBalanceOf) {
json.add({
"constant": true,
"inputs": [
{"name": "", "type": "address"}
],
"name": "balanceOf",
"outputs": [
{"name": "", "type": "uint256"}
],
"payable": false,
"type": "function"
});
}
if (hackInTransfer) {
json.add({
"constant": false,
"inputs": [
{"name": "_to", "type": "address"},
{"name": "_value", "type": "uint256"}
],
"name": "transfer",
"outputs": <dynamic>[],
"payable": false,
"type": "function"
});
}
_tokenAbi = jsonEncode(json);
await _secureStore.write(
key: '${_contractAddress.hex}_tokenAbi', value: _tokenAbi);
_contract = web3dart.DeployedContract(
web3dart.ContractAbi.fromJson(
_tokenAbi,
token.name,
),
_contractAddress,
);
_contract = web3dart.DeployedContract(
web3dart.ContractAbi.fromJson(_tokenAbi, token.name),
_contractAddress);
_balanceFunction = _contract.function('balanceOf');
_sendFunction = _contract.function('transfer');
}
_balanceFunction = _contract.function('balanceOf');
_sendFunction = _contract.function('transfer');
_client = await getEthClient();