mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-10 20:54:33 +00:00
get token abi fixes
This commit is contained in:
parent
16efeea1db
commit
8466180b47
2 changed files with 112 additions and 89 deletions
|
@ -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 {
|
||||
try {
|
||||
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>);
|
||||
final json = jsonDecode(response.body);
|
||||
if (json["message"] == "OK") {
|
||||
return EthereumResponse(
|
||||
json["result"] as String,
|
||||
null,
|
||||
);
|
||||
} else {
|
||||
throw Exception("ERROR GETTING TOKENABI ${response.reasonPhrase}");
|
||||
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()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 (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);
|
||||
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!;
|
||||
}
|
||||
}
|
||||
|
||||
_contract = web3dart.DeployedContract(
|
||||
web3dart.ContractAbi.fromJson(_tokenAbi, token.name),
|
||||
_contractAddress);
|
||||
web3dart.ContractAbi.fromJson(
|
||||
_tokenAbi,
|
||||
token.name,
|
||||
),
|
||||
_contractAddress,
|
||||
);
|
||||
|
||||
_balanceFunction = _contract.function('balanceOf');
|
||||
_sendFunction = _contract.function('transfer');
|
||||
}
|
||||
|
||||
_client = await getEthClient();
|
||||
|
||||
|
|
Loading…
Reference in a new issue