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