Improve gas calc (#3356)

* update to use Infura gas provider if available.
* Ensure up to date gas information has been received before sending transactions.
pull/3357/head
James Brown 9 months ago committed by GitHub
parent 80e6ae788f
commit 9677256493
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      app/build.gradle
  2. 13
      app/src/main/java/com/alphawallet/app/entity/ActionSheetInterface.java
  3. 62
      app/src/main/java/com/alphawallet/app/entity/GasPriceSpread.java
  4. 12
      app/src/main/java/com/alphawallet/app/repository/AWRealmMigration.java
  5. 26
      app/src/main/java/com/alphawallet/app/repository/EthereumNetworkBase.java
  6. 5
      app/src/main/java/com/alphawallet/app/repository/HttpServiceHelper.java
  7. 2
      app/src/main/java/com/alphawallet/app/repository/TransactionsRealmCache.java
  8. 7
      app/src/main/java/com/alphawallet/app/repository/entity/Realm1559Gas.java
  9. 6
      app/src/main/java/com/alphawallet/app/service/BlockNativeGasAPI.java
  10. 11
      app/src/main/java/com/alphawallet/app/service/GasService.java
  11. 126
      app/src/main/java/com/alphawallet/app/service/InfuraGasAPI.java
  12. 6
      app/src/main/java/com/alphawallet/app/ui/widget/entity/GasWidgetInterface.java
  13. 30
      app/src/main/java/com/alphawallet/app/widget/ActionSheetDialog.java
  14. 45
      app/src/main/java/com/alphawallet/app/widget/GasWidget.java
  15. 46
      app/src/main/java/com/alphawallet/app/widget/GasWidget2.java
  16. 9
      app/src/main/res/layout/item_gas_settings.xml

@ -90,7 +90,7 @@ android {
def DEFUALT_WALLETCONNECT_PROJECT_ID = "\"40c6071febfd93f4fe485c232a8a4cd9\""
def DEFAULT_AURORA_API_KEY = "\"HFDDY5BNKGXBB82DE2G8S64C3C41B76PYI\""; //Put your Aurorascan.dev API key here - this one will rate limit as it is common
buildConfigField 'int', 'DB_VERSION', '53'
buildConfigField 'int', 'DB_VERSION', '54'
buildConfigField "String", XInfuraAPI, DEFAULT_INFURA_API_KEY
buildConfigField "String", "WALLETCONNECT_PROJECT_ID", DEFUALT_WALLETCONNECT_PROJECT_ID

@ -1,6 +1,9 @@
package com.alphawallet.app.entity;
import android.content.Intent;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import com.alphawallet.app.web3.entity.Web3Transaction;
@ -58,4 +61,14 @@ public interface ActionSheetInterface
default void setCurrentGasIndex(ActivityResult result)
{
}
default ActivityResultLauncher<Intent> gasSelectLauncher()
{
return null;
}
default void gasEstimateReady()
{
}
}

@ -19,6 +19,8 @@ import java.util.Map;
import javax.annotation.Nullable;
import timber.log.Timber;
/**
* Created by JB on 20/01/2022.
*/
@ -165,6 +167,66 @@ public class GasPriceSpread implements Parcelable
hasLockedGas = false;
}
public GasPriceSpread(Context ctx, String apiReturn) //ChainId is unused but we need to disambiguate from etherscan API return
{
this.timeStamp = System.currentTimeMillis();
BigDecimal rBaseFee = BigDecimal.ZERO;
hasLockedGas = false;
try
{
JSONObject result = new JSONObject(apiReturn);
if (result.has("estimatedBaseFee"))
{
rBaseFee = new BigDecimal(result.getString("estimatedBaseFee"));
}
EIP1559FeeOracleResult low = readFeeResult(result, "low", rBaseFee);
EIP1559FeeOracleResult medium = readFeeResult(result, "medium", rBaseFee);
EIP1559FeeOracleResult high = readFeeResult(result, "high", rBaseFee);
if (low == null || medium == null || high == null)
{
return;
}
BigInteger rapidPriorityFee = (new BigDecimal(high.priorityFee)).multiply(BigDecimal.valueOf(1.2)).toBigInteger();
EIP1559FeeOracleResult rapid = new EIP1559FeeOracleResult(high.maxFeePerGas, rapidPriorityFee, gweiToWei(rBaseFee));
fees.put(TXSpeed.SLOW, new GasSpeed(ctx.getString(R.string.speed_slow), SLOW_SECONDS, low));
fees.put(TXSpeed.STANDARD, new GasSpeed(ctx.getString(R.string.speed_average), STANDARD_SECONDS, medium));
fees.put(TXSpeed.FAST, new GasSpeed(ctx.getString(R.string.speed_fast), FAST_SECONDS, high));
fees.put(TXSpeed.RAPID, new GasSpeed(ctx.getString(R.string.speed_rapid), RAPID_SECONDS, rapid));
}
catch (JSONException e)
{
//
}
}
private EIP1559FeeOracleResult readFeeResult(JSONObject result, String speed, BigDecimal rBaseFee)
{
EIP1559FeeOracleResult oracleResult = null;
try
{
if (result.has(speed))
{
JSONObject thisSpeed = result.getJSONObject(speed);
BigDecimal maxFeePerGas = new BigDecimal(thisSpeed.getString("suggestedMaxFeePerGas"));
BigDecimal priorityFee = new BigDecimal(thisSpeed.getString("suggestedMaxPriorityFeePerGas"));
oracleResult = new EIP1559FeeOracleResult(gweiToWei(maxFeePerGas), gweiToWei(priorityFee), gweiToWei(rBaseFee));
}
}
catch (Exception e)
{
Timber.e("Infura GasOracle read failing; please adjust your Infura API settings.");
}
return oracleResult;
}
// For etherscan return
public GasPriceSpread(String apiReturn)
{
this.timeStamp = System.currentTimeMillis();

@ -490,6 +490,18 @@ public class AWRealmMigration implements RealmMigration
oldVersion = 53;
}
if (oldVersion == 53)
{
RealmObjectSchema realmData = schema.get("Realm1559Gas");
if (realmData != null) schema.remove("Realm1559Gas");
schema.create("Realm1559Gas")
.addField("chainId", long.class, FieldAttribute.PRIMARY_KEY)
.addField("timeStamp", long.class)
.addField("resultData", String.class);
oldVersion = 54;
}
}
@Override

@ -119,6 +119,7 @@ public abstract class EthereumNetworkBase implements EthereumNetworkRepositoryTy
private static final KeyProvider keyProvider = KeyProviderFactory.get();
public static final boolean usesProductionKey = !keyProvider.getInfuraKey().equals(DEFAULT_INFURA_KEY);
private static final String INFURA_GAS_API = "https://gas.api.infura.io/networks/CHAIN_ID/suggestedGasFees";
public static final String FREE_MAINNET_RPC_URL = "https://rpc.ankr.com/eth";
public static final String FREE_POLYGON_RPC_URL = "https://polygon-rpc.com";
@ -493,7 +494,9 @@ public abstract class EthereumNetworkBase implements EthereumNetworkRepositoryTy
//Add it to this list here if so. Note that so far, all gas oracles follow the same format:
// <etherscanAPI from the above list> + GAS_API
//If the gas oracle you're adding doesn't follow this spec then you'll have to change the getGasOracle method
private static final List<Long> hasGasOracleAPI = Arrays.asList(MAINNET_ID, HECO_ID, BINANCE_MAIN_ID, POLYGON_ID);
private static final List<Long> hasGasOracleAPI = Arrays.asList(MAINNET_ID, POLYGON_ID, ARBITRUM_MAIN_ID, AVALANCHE_ID, BINANCE_MAIN_ID, CRONOS_MAIN_ID, GOERLI_ID,
SEPOLIA_TESTNET_ID, FANTOM_ID, LINEA_ID, OPTIMISTIC_MAIN_ID, POLYGON_TEST_ID);
private static final List<Long> hasEtherscanGasOracleAPI = Arrays.asList(MAINNET_ID, HECO_ID, BINANCE_MAIN_ID, POLYGON_ID);
private static final List<Long> hasBlockNativeGasOracleAPI = Arrays.asList(MAINNET_ID, POLYGON_ID);
//These chains don't allow custom gas
private static final List<Long> hasLockedGas = Arrays.asList(KLAYTN_ID, KLAYTN_BAOBAB_ID);
@ -508,11 +511,24 @@ public abstract class EthereumNetworkBase implements EthereumNetworkRepositoryTy
}
};
public static String getEtherscanGasOracle(long chainId)
{
if (hasEtherscanGasOracleAPI.contains(chainId) && networkMap.indexOfKey(chainId) >= 0)
{
return networkMap.get(chainId).etherscanAPI + GAS_API;
}
else
{
return "";
}
}
public static String getGasOracle(long chainId)
{
if (hasGasOracleAPI.contains(chainId) && networkMap.indexOfKey(chainId) >= 0)
{
return networkMap.get(chainId).etherscanAPI + GAS_API;
//construct API route:
return INFURA_GAS_API.replace("CHAIN_ID", Long.toString(chainId));
}
else
{
@ -603,8 +619,10 @@ public abstract class EthereumNetworkBase implements EthereumNetworkRepositoryTy
public static int getBatchProcessingLimit(long chainId)
{
if (batchProcessingLimitMap.size() == 0) setBatchProcessingLimits(); //If batch limits not set, init them and proceed
{
return batchProcessingLimitMap.get(chainId, 0); //default to zero / no batching
}
}
@Override
public boolean hasLockedGas(long chainId)
@ -861,8 +879,6 @@ public abstract class EthereumNetworkBase implements EthereumNetworkRepositoryTy
return networkMap.get(chainId);
}
// fetches the last transaction nonce; if it's identical to the last used one then increment by one
// to ensure we don't get transaction replacement
@Override
public Single<BigInteger> getLastTransactionNonce(Web3j web3j, String walletAddress)
{
@ -871,7 +887,7 @@ public abstract class EthereumNetworkBase implements EthereumNetworkRepositoryTy
try
{
EthGetTransactionCount ethGetTransactionCount = web3j
.ethGetTransactionCount(walletAddress, DefaultBlockParameterName.LATEST)
.ethGetTransactionCount(walletAddress, DefaultBlockParameterName.PENDING)
.send();
return ethGetTransactionCount.getTransactionCount();
}

@ -38,4 +38,9 @@ public class HttpServiceHelper
service.addHeader("Authorization", "Basic " + infuraKey);
}
}
public static void addInfuraGasCredentials(Request.Builder service, String infuraSecret)
{
service.addHeader("Authorization", "Basic " + infuraSecret);
}
}

@ -10,6 +10,7 @@ import com.alphawallet.app.entity.EventMeta;
import com.alphawallet.app.entity.Transaction;
import com.alphawallet.app.entity.TransactionMeta;
import com.alphawallet.app.entity.Wallet;
import com.alphawallet.app.repository.entity.Realm1559Gas;
import com.alphawallet.app.repository.entity.RealmAuxData;
import com.alphawallet.app.repository.entity.RealmNFTAsset;
import com.alphawallet.app.repository.entity.RealmToken;
@ -295,6 +296,7 @@ public class TransactionsRealmCache implements TransactionLocalSource {
r.where(RealmAuxData.class).findAll().deleteAllFromRealm();
r.where(RealmNFTAsset.class).findAll().deleteAllFromRealm();
r.where(RealmTransfer.class).findAll().deleteAllFromRealm();
r.where(Realm1559Gas.class).findAll().deleteAllFromRealm();
});
instance.refresh();
}

@ -24,7 +24,12 @@ public class Realm1559Gas extends RealmObject
public Map<Integer, EIP1559FeeOracleResult> getResult()
{
Type entry = new TypeToken<Map<Integer, EIP1559FeeOracleResult>>() {}.getType();
return new Gson().fromJson(resultData, entry);
return new Gson().fromJson(getResultData(), entry);
}
public String getResultData()
{
return resultData;
}
public void setResultData(Map<Integer, EIP1559FeeOracleResult> result, long ts)

@ -55,8 +55,12 @@ public class BlockNativeGasAPI
return requestB.build();
}
public Single<Map<Integer, EIP1559FeeOracleResult>> fetchGasEstimates(long chainId)
public Single<Map<Integer, EIP1559FeeOracleResult>> get1559GasEstimates(Map<Integer, EIP1559FeeOracleResult> result, long chainId)
{
if (result.size() > 0)
{
return Single.fromCallable(() -> result);
}
String oracleAPI = EthereumNetworkBase.getBlockNativeOracle(chainId);
return Single.fromCallable(() -> buildOracleResult(executeRequest(oracleAPI))); // any kind of error results in blank mapping,
// if blank, fall back to calculation method

@ -30,7 +30,6 @@ import com.alphawallet.app.repository.KeyProviderFactory;
import com.alphawallet.app.repository.entity.Realm1559Gas;
import com.alphawallet.app.repository.entity.RealmGasSpread;
import com.alphawallet.app.web3.entity.Web3Transaction;
import org.web3j.utils.Numeric;
import com.google.gson.Gson;
import org.jetbrains.annotations.Nullable;
@ -41,6 +40,7 @@ import org.web3j.protocol.core.methods.response.EthEstimateGas;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.gas.ContractGasProvider;
import org.web3j.utils.Numeric;
import java.math.BigInteger;
import java.util.Map;
@ -82,6 +82,7 @@ public class GasService implements ContractGasProvider
private final String ETHERSCAN_API_KEY;
private final String POLYGONSCAN_API_KEY;
private boolean keyFail;
@Nullable
private Disposable gasFetchDisposable;
@ -186,7 +187,7 @@ public class GasService implements ContractGasProvider
private Single<Boolean> updateCurrentGasPrices()
{
String gasOracleAPI = EthereumNetworkRepository.getGasOracle(currentChainId);
String gasOracleAPI = EthereumNetworkRepository.getEtherscanGasOracle(currentChainId);
if (!TextUtils.isEmpty(gasOracleAPI))
{
if (!keyFail && gasOracleAPI.contains("etherscan")) gasOracleAPI += ETHERSCAN_API_KEY;
@ -303,13 +304,14 @@ public class GasService implements ContractGasProvider
Realm1559Gas rgs = r.where(Realm1559Gas.class)
.equalTo("chainId", chainId)
.findFirst();
if (rgs == null)
{
rgs = r.createObject(Realm1559Gas.class, chainId);
}
rgs.setResultData(result, System.currentTimeMillis());
r.insertOrUpdate(rgs);
//r.insertOrUpdate(rgs);
});
}
catch (Exception e)
@ -420,7 +422,8 @@ public class GasService implements ContractGasProvider
private Single<Map<Integer, EIP1559FeeOracleResult>> getEIP1559FeeStructure()
{
return BlockNativeGasAPI.get(httpClient).fetchGasEstimates(currentChainId)
return InfuraGasAPI.get1559GasEstimates(currentChainId, httpClient)
.flatMap(result -> BlockNativeGasAPI.get(httpClient).get1559GasEstimates(result, currentChainId))
.flatMap(this::useCalculationIfRequired); //if interface doesn't have blocknative API then use calculation method
}

@ -0,0 +1,126 @@
package com.alphawallet.app.service;
import static com.alphawallet.app.util.BalanceUtils.gweiToWei;
import android.text.TextUtils;
import com.alphawallet.app.entity.EIP1559FeeOracleResult;
import com.alphawallet.app.repository.EthereumNetworkRepository;
import com.alphawallet.app.repository.HttpServiceHelper;
import com.alphawallet.app.repository.KeyProviderFactory;
import org.json.JSONException;
import org.json.JSONObject;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import io.reactivex.Single;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber;
public class InfuraGasAPI
{
public static Single<Map<Integer, EIP1559FeeOracleResult>> get1559GasEstimates(final long chainId, final OkHttpClient httpClient)
{
return Single.fromCallable(() -> {
Map<Integer, EIP1559FeeOracleResult> gasMap = new HashMap<>();
//ensure we have correct Infura details
String gasOracleAPI = EthereumNetworkRepository.getGasOracle(chainId);
String infuraKey = KeyProviderFactory.get().getInfuraKey();
String infuraSecret = KeyProviderFactory.get().getInfuraSecret();
if (TextUtils.isEmpty(gasOracleAPI) || TextUtils.isEmpty(infuraKey) || TextUtils.isEmpty(infuraSecret))
{
//require Infura key with API and secret to operate the gas API
return gasMap;
}
final Request.Builder rqBuilder = new Request.Builder()
.url(gasOracleAPI)
.get();
HttpServiceHelper.addInfuraGasCredentials(rqBuilder, KeyProviderFactory.get().getInfuraSecret());
try (Response response = httpClient.newCall(rqBuilder.build()).execute())
{
if (response.code() / 200 == 1)
{
String result = response.body()
.string();
gasMap = readGasMap(result);
}
}
catch (Exception e)
{
Timber.w(e);
}
return gasMap;
});
}
private static Map<Integer, EIP1559FeeOracleResult> readGasMap(String apiReturn)
{
Map<Integer, EIP1559FeeOracleResult> gasMap = new HashMap<>();
try
{
BigDecimal rBaseFee = BigDecimal.ZERO;
JSONObject result = new JSONObject(apiReturn);
if (result.has("estimatedBaseFee"))
{
rBaseFee = new BigDecimal(result.getString("estimatedBaseFee"));
}
EIP1559FeeOracleResult low = readFeeResult(result, "low", rBaseFee);
EIP1559FeeOracleResult medium = readFeeResult(result, "medium", rBaseFee);
EIP1559FeeOracleResult high = readFeeResult(result, "high", rBaseFee);
if (low == null || medium == null || high == null)
{
return gasMap;
}
BigInteger rapidPriorityFee = (new BigDecimal(high.priorityFee)).multiply(BigDecimal.valueOf(1.2)).toBigInteger();
EIP1559FeeOracleResult rapid = new EIP1559FeeOracleResult(high.maxFeePerGas, rapidPriorityFee, gweiToWei(rBaseFee));
gasMap.put(0, rapid);
gasMap.put(1, high);
gasMap.put(2, medium);
gasMap.put(3, low);
}
catch (JSONException e)
{
//
}
return gasMap;
}
private static EIP1559FeeOracleResult readFeeResult(JSONObject result, String speed, BigDecimal rBaseFee)
{
EIP1559FeeOracleResult oracleResult = null;
try
{
if (result.has(speed))
{
JSONObject thisSpeed = result.getJSONObject(speed);
BigDecimal maxFeePerGas = new BigDecimal(thisSpeed.getString("suggestedMaxFeePerGas"));
BigDecimal priorityFee = new BigDecimal(thisSpeed.getString("suggestedMaxPriorityFeePerGas"));
oracleResult = new EIP1559FeeOracleResult(gweiToWei(maxFeePerGas), gweiToWei(priorityFee), gweiToWei(rBaseFee));
}
}
catch (Exception e)
{
Timber.e("Infura GasOracle read failing; please adjust your Infura API settings.");
}
return oracleResult;
}
}

@ -28,4 +28,10 @@ public interface GasWidgetInterface
void setupResendSettings(ActionSheetMode mode, BigInteger gasPrice);
void setCurrentGasIndex(int gasSelectionIndex, BigInteger maxFeePerGas, BigInteger maxPriorityFee, BigDecimal customGasLimit, long expectedTxTime, long customNonce);
long getExpectedTransactionTime();
default boolean gasPriceReady(long gasEstimateTime)
{
return gasEstimateTime > (System.currentTimeMillis() - 30 * 1000);
}
boolean gasPriceReady();
}

@ -10,6 +10,7 @@ import android.view.View;
import android.widget.Toast;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
@ -89,7 +90,6 @@ public class ActionSheetDialog extends ActionSheet implements StandardFunctionIn
private boolean use1559Transactions = false;
private Transaction transaction;
private final WalletType walletType;
private Disposable disposable;
public ActionSheetDialog(@NonNull Activity activity, Web3Transaction tx, Token t,
String destName, String destAddress, TokensService ts,
@ -329,14 +329,14 @@ public class ActionSheetDialog extends ActionSheet implements StandardFunctionIn
if (use1559Transactions)
{
gasWidget.setupWidget(tokensService, token, candidateTransaction, actionSheetCallback.gasSelectLauncher());
gasWidget.setupWidget(tokensService, token, candidateTransaction, this);
return gasWidget;
}
else
{
gasWidget.setVisibility(View.GONE);
gasWidgetLegacy.setVisibility(View.VISIBLE);
gasWidgetLegacy.setupWidget(tokensService, token, candidateTransaction, this, actionSheetCallback.gasSelectLauncher());
gasWidgetLegacy.setupWidget(tokensService, token, candidateTransaction, this, this);
return gasWidgetLegacy;
}
}
@ -434,6 +434,13 @@ public class ActionSheetDialog extends ActionSheet implements StandardFunctionIn
@SuppressWarnings("checkstyle:MissingSwitchDefault")
public void handleClick(String action, int id)
{
//first ensure gas estimate is up to date
if (gasEstimateOutOfDate())
{
functionBar.setPrimaryButtonWaiting();
return;
}
if (walletType == WalletType.HARDWARE)
{
//TODO: Hardware - Maybe flick a toast to tell user to apply card
@ -477,6 +484,23 @@ public class ActionSheetDialog extends ActionSheet implements StandardFunctionIn
}
}
@Override
public void gasEstimateReady()
{
functionBar.setPrimaryButtonEnabled(true);
}
@Override
public ActivityResultLauncher<Intent> gasSelectLauncher()
{
return actionSheetCallback.gasSelectLauncher();
}
private boolean gasEstimateOutOfDate()
{
return !gasWidget.gasPriceReady();
}
private BigDecimal getTransactionAmount()
{
BigDecimal txAmount;

@ -11,11 +11,10 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import com.alphawallet.app.BuildConfig;
import com.alphawallet.app.C;
import com.alphawallet.app.R;
import com.alphawallet.app.entity.ActionSheetInterface;
import com.alphawallet.app.entity.GasPriceSpread;
import com.alphawallet.app.entity.StandardFunctionInterface;
import com.alphawallet.app.entity.TXSpeed;
@ -70,6 +69,8 @@ public class GasWidget extends LinearLayout implements Runnable, GasWidgetInterf
private long customNonce = -1;
private boolean isSendingAll;
private BigInteger resendGasPrice = BigInteger.ZERO;
long gasEstimateTime = 0;
private ActionSheetInterface actionSheetInterface;
public GasWidget(Context ctx, AttributeSet attrs)
{
@ -85,7 +86,7 @@ public class GasWidget extends LinearLayout implements Runnable, GasWidgetInterf
//For legacy transaction, either we are sending all or the chain doesn't support EIP1559
//Since these chains are not so well used, we will compromise and send at the standard gas rate
//That is - not allow selection of gas price
public void setupWidget(TokensService svs, Token t, Web3Transaction tx, StandardFunctionInterface sfi, ActivityResultLauncher<Intent> gasSelectLauncher)
public void setupWidget(TokensService svs, Token t, Web3Transaction tx, StandardFunctionInterface sfi, ActionSheetInterface actionSheetIf)
{
tokensService = svs;
token = t;
@ -96,6 +97,7 @@ public class GasWidget extends LinearLayout implements Runnable, GasWidgetInterf
isSendingAll = isSendingAll(tx);
initialGasPrice = tx.gasPrice;
customNonce = tx.nonce;
actionSheetInterface = actionSheetIf;
if (tx.gasLimit.equals(BigInteger.ZERO)) //dapp didn't specify a limit, use default limits until node returns an estimate (see setGasEstimate())
{
@ -133,23 +135,28 @@ public class GasWidget extends LinearLayout implements Runnable, GasWidgetInterf
intent.putExtra(C.EXTRA_NONCE, customNonce);
intent.putExtra(C.EXTRA_1559_TX, false);
intent.putExtra(C.EXTRA_MIN_GAS_PRICE, resendGasPrice.longValue());
gasSelectLauncher.launch(intent);
actionSheetInterface.gasSelectLauncher().launch(intent);
});
}
}
private void setupGasSpeeds(Web3Transaction w3tx)
{
RealmGasSpread rgs = getGasQuery().findFirst();
if (rgs != null)
try (Realm realm = tokensService.getTickerRealmInstance())
{
initGasSpeeds(rgs);
RealmGasSpread gasReturn = realm.where(RealmGasSpread.class)
.equalTo("chainId", token.tokenInfo.chainId).findFirst();
if (gasReturn != null)
{
initGasSpeeds(gasReturn);
}
else
{
// Couldn't get current gas. Add a blank custom gas speed node
gasSpread = new GasPriceSpread(getContext(), w3tx.gasPrice);
}
}
if (w3tx.gasPrice.compareTo(BigInteger.ZERO) > 0)
{
@ -301,6 +308,8 @@ public class GasWidget extends LinearLayout implements Runnable, GasWidgetInterf
TextView editTxt = findViewById(R.id.edit_text);
gasEstimateTime = rgs.getTimeStamp();
if (gasSpread.hasLockedGas() && editTxt.getVisibility() == View.VISIBLE)
{
findViewById(R.id.edit_text).setVisibility(View.GONE);
@ -383,6 +392,16 @@ public class GasWidget extends LinearLayout implements Runnable, GasWidgetInterf
}
checkSufficientGas();
manageWarnings();
if (gasPriceReady(gasEstimateTime))
{
actionSheetInterface.gasEstimateReady();
setGasReadyStatus(true);
}
else
{
setGasReadyStatus(false);
}
}
@Override
@ -399,6 +418,12 @@ public class GasWidget extends LinearLayout implements Runnable, GasWidgetInterf
}
}
@Override
public boolean gasPriceReady()
{
return gasPriceReady(gasEstimateTime);
}
@Override
public BigInteger getValue()
{
@ -447,6 +472,12 @@ public class GasWidget extends LinearLayout implements Runnable, GasWidgetInterf
}
}
private void setGasReadyStatus(boolean ready)
{
findViewById(R.id.view_spacer).setVisibility(ready ? View.VISIBLE : View.GONE);
findViewById(R.id.gas_fetch_wait).setVisibility(ready ? View.GONE : View.VISIBLE);
}
private void showCustomSpeedWarning(boolean high)
{
if (currentGasSpeedIndex != TXSpeed.CUSTOM)

@ -11,11 +11,10 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import com.alphawallet.app.BuildConfig;
import com.alphawallet.app.C;
import com.alphawallet.app.R;
import com.alphawallet.app.entity.ActionSheetInterface;
import com.alphawallet.app.entity.GasPriceSpread;
import com.alphawallet.app.entity.TXSpeed;
import com.alphawallet.app.entity.analytics.ActionSheetMode;
@ -66,6 +65,8 @@ public class GasWidget2 extends LinearLayout implements Runnable, GasWidgetInter
private TXSpeed currentGasSpeedIndex = TXSpeed.STANDARD;
private long customNonce = -1;
private BigInteger resendGasPrice = BigInteger.ZERO;
private long gasEstimateTime = 0;
private ActionSheetInterface actionSheetInterface;
//Need to track user selected gas limit & calculated gas limit
//At initial setup, we have the limit from the tx or default: presetGasLimit
@ -85,7 +86,7 @@ public class GasWidget2 extends LinearLayout implements Runnable, GasWidgetInter
}
// Called once from ActionSheet constructor
public void setupWidget(TokensService svs, Token t, Web3Transaction tx, ActivityResultLauncher<Intent> gasSelectLauncher)
public void setupWidget(TokensService svs, Token t, Web3Transaction tx, ActionSheetInterface actionSheetIf)
{
tokensService = svs;
token = t;
@ -93,6 +94,7 @@ public class GasWidget2 extends LinearLayout implements Runnable, GasWidgetInter
adjustedValue = tx.value;
initialGasPrice = tx.gasPrice;
customNonce = tx.nonce;
actionSheetInterface = actionSheetIf;
if (tx.gasLimit.equals(BigInteger.ZERO)) //dapp didn't specify a limit, use default limits until node returns an estimate (see setGasEstimate())
{
@ -124,7 +126,7 @@ public class GasWidget2 extends LinearLayout implements Runnable, GasWidgetInter
intent.putExtra(C.EXTRA_NONCE, customNonce);
intent.putExtra(C.EXTRA_1559_TX, true);
intent.putExtra(C.EXTRA_MIN_GAS_PRICE, resendGasPrice.longValue());
gasSelectLauncher.launch(intent);
actionSheetInterface.gasSelectLauncher().launch(intent);
});
}
}
@ -132,16 +134,21 @@ public class GasWidget2 extends LinearLayout implements Runnable, GasWidgetInter
//set custom fee if specified by tx feed
private void setupGasSpeeds(Web3Transaction w3tx)
{
Realm1559Gas getGas = getGasQuery2().findFirst();
if (getGas != null)
try (Realm realm = tokensService.getTickerRealmInstance())
{
Realm1559Gas gasReturn = realm.where(Realm1559Gas.class)
.equalTo("chainId", token.tokenInfo.chainId).findFirst();
if (gasReturn != null)
{
initGasSpeeds(getGas);
initGasSpeeds(gasReturn);
}
else
{
// Couldn't get current gas. Add a blank custom gas speed node
gasSpread = new GasPriceSpread(getContext(), w3tx.maxFeePerGas, w3tx.maxPriorityFeePerGas);
}
}
if (w3tx.maxFeePerGas.compareTo(BigInteger.ZERO) > 0 && w3tx.maxPriorityFeePerGas.compareTo(BigInteger.ZERO) > 0)
{
@ -273,6 +280,7 @@ public class GasWidget2 extends LinearLayout implements Runnable, GasWidgetInter
GasSpeed custom = getCustomGasSpeed();
gasSpread = new GasPriceSpread(getContext(), gs.getResult());
gasSpread.setCustom(custom);
gasEstimateTime = gs.getTimeStamp();
//if we have mainnet then show timings, otherwise no timing, if the token has fiat value, show fiat value of gas, so we need the ticker
handler.post(this);
@ -340,6 +348,22 @@ public class GasWidget2 extends LinearLayout implements Runnable, GasWidgetInter
}
checkSufficientGas();
manageWarnings();
if (gasPriceReady(gasEstimateTime))
{
actionSheetInterface.gasEstimateReady();
setGasReadyStatus(true);
}
else
{
setGasReadyStatus(false);
}
}
private void setGasReadyStatus(boolean ready)
{
findViewById(R.id.view_spacer).setVisibility(ready ? View.VISIBLE : View.GONE);
findViewById(R.id.gas_fetch_wait).setVisibility(ready ? View.GONE : View.VISIBLE);
}
@Override
@ -359,7 +383,7 @@ public class GasWidget2 extends LinearLayout implements Runnable, GasWidgetInter
@Override
public BigInteger getGasPrice(BigInteger defaultPrice)
{
if (gasSpread != null)
if (gasSpread != null && gasSpread.getSelectedGasFee(currentGasSpeedIndex) != null)
{
GasSpeed gs = gasSpread.getSelectedGasFee(currentGasSpeedIndex);
return gs.gasPrice.maxFeePerGas;
@ -390,6 +414,12 @@ public class GasWidget2 extends LinearLayout implements Runnable, GasWidgetInter
return false;
}
@Override
public boolean gasPriceReady()
{
return gasPriceReady(gasEstimateTime);
}
@Override
public BigInteger getValue()
{

@ -110,10 +110,19 @@
android:paddingEnd="@dimen/mini_4">
<View
android:id="@+id/view_spacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="@integer/widget_label" />
<ProgressBar
android:id="@+id/gas_fetch_wait"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:progressDrawable="@drawable/progress_bar_spinner"
android:visibility="gone"
android:layout_weight="@integer/widget_control"/>
<TextView
android:id="@+id/text_time_estimate"
style="@style/Aw.Typography.Sub"

Loading…
Cancel
Save