Refactor gas handling and fix some issues (#3272)
parent
730448cd27
commit
ed094eb78b
@ -0,0 +1,166 @@ |
||||
package com.alphawallet.app.service; |
||||
|
||||
import android.text.TextUtils; |
||||
|
||||
import com.alphawallet.app.entity.EIP1559FeeOracleResult; |
||||
import com.alphawallet.app.repository.EthereumNetworkBase; |
||||
import com.alphawallet.app.repository.KeyProviderFactory; |
||||
import com.alphawallet.app.util.BalanceUtils; |
||||
import com.alphawallet.app.util.JsonUtils; |
||||
import com.google.gson.Gson; |
||||
|
||||
import org.json.JSONArray; |
||||
import org.json.JSONException; |
||||
import org.json.JSONObject; |
||||
|
||||
import java.io.IOException; |
||||
import java.math.BigDecimal; |
||||
import java.math.BigInteger; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Objects; |
||||
|
||||
import io.reactivex.Single; |
||||
import okhttp3.OkHttpClient; |
||||
import okhttp3.Request; |
||||
import okhttp3.ResponseBody; |
||||
import timber.log.Timber; |
||||
|
||||
public class BlockNativeGasAPI |
||||
{ |
||||
public static BlockNativeGasAPI instance; |
||||
private final OkHttpClient httpClient; |
||||
|
||||
public static BlockNativeGasAPI get(OkHttpClient httpClient) |
||||
{ |
||||
if (instance == null) |
||||
{ |
||||
instance = new BlockNativeGasAPI(httpClient); |
||||
} |
||||
return instance; |
||||
} |
||||
|
||||
public BlockNativeGasAPI(OkHttpClient httpClient) |
||||
{ |
||||
this.httpClient = httpClient; |
||||
} |
||||
|
||||
private Request buildRequest(String api) |
||||
{ |
||||
Request.Builder requestB = new Request.Builder() |
||||
.url(api) |
||||
.header("Content-Type", "application/json") |
||||
.addHeader("Authorization", KeyProviderFactory.get().getBlockNativeKey()) |
||||
.get(); |
||||
return requestB.build(); |
||||
} |
||||
|
||||
public Single<Map<Integer, EIP1559FeeOracleResult>> fetchGasEstimates(long chainId) |
||||
{ |
||||
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
|
||||
} |
||||
|
||||
private Map<Integer, EIP1559FeeOracleResult> buildOracleResult(String oracleReturn) |
||||
{ |
||||
Map<Integer, EIP1559FeeOracleResult> results = new HashMap<>(); |
||||
try |
||||
{ |
||||
JSONObject prices = new JSONObject(oracleReturn); |
||||
//get base fee per gas
|
||||
JSONArray blockPrices = prices.getJSONArray("blockPrices"); |
||||
JSONObject blockPrice0 = blockPrices.getJSONObject(0); |
||||
String baseFeePerGasStr = blockPrice0.getString("baseFeePerGas"); |
||||
BigDecimal baseFeePerGas = new BigDecimal(baseFeePerGasStr); |
||||
BigInteger baseFeePerGasWei = BalanceUtils.gweiToWei(baseFeePerGas); |
||||
//get the array
|
||||
String estimatedPrices = blockPrice0.getJSONArray("estimatedPrices").toString(); |
||||
PriceElement[] priceElements = new Gson().fromJson(estimatedPrices, PriceElement[].class); |
||||
|
||||
results.put(0, new EIP1559FeeOracleResult(priceElements[0].getFeeOracleResult(baseFeePerGasWei))); |
||||
results.put(1, new EIP1559FeeOracleResult(priceElements[2].getFeeOracleResult(baseFeePerGasWei))); |
||||
results.put(2, new EIP1559FeeOracleResult(priceElements[3].getFeeOracleResult(baseFeePerGasWei))); |
||||
results.put(3, new EIP1559FeeOracleResult(priceElements[4].getFeeOracleResult(baseFeePerGasWei))); |
||||
} |
||||
catch (JSONException e) |
||||
{ |
||||
// map will be empty; default to using backup calculation method
|
||||
Timber.w(e); |
||||
} |
||||
return results; |
||||
} |
||||
|
||||
private String executeRequest(String api) |
||||
{ |
||||
if (!TextUtils.isEmpty(api)) |
||||
{ |
||||
try (okhttp3.Response response = httpClient.newCall(buildRequest(api)).execute()) |
||||
{ |
||||
if (response.isSuccessful()) |
||||
{ |
||||
ResponseBody responseBody = response.body(); |
||||
if (responseBody != null) |
||||
{ |
||||
return responseBody.string(); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
return Objects.requireNonNull(response.body()).string(); |
||||
} |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
Timber.e(e); |
||||
return e.getMessage(); |
||||
} |
||||
} |
||||
|
||||
return JsonUtils.EMPTY_RESULT; |
||||
} |
||||
|
||||
private static class PriceElement |
||||
{ |
||||
public String confidence; |
||||
public String price; |
||||
public String maxPriorityFeePerGas; |
||||
public String maxFeePerGas; |
||||
|
||||
public BigInteger getMaxPriorityFeePerGasWei() |
||||
{ |
||||
return elementToWei(maxPriorityFeePerGas); |
||||
} |
||||
|
||||
public BigInteger getMaxFeePerGasWei() |
||||
{ |
||||
return elementToWei(maxFeePerGas); |
||||
} |
||||
|
||||
private BigInteger elementToWei(String value) |
||||
{ |
||||
try |
||||
{ |
||||
BigDecimal gweiValue = new BigDecimal(value); |
||||
return BalanceUtils.gweiToWei(gweiValue); |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
return BigInteger.ZERO; |
||||
} |
||||
} |
||||
|
||||
/* |
||||
public EIP1559FeeOracleResult(BigInteger maxFee, BigInteger maxPriority, BigInteger base) |
||||
{ |
||||
maxFeePerGas = fixGasPriceReturn(maxFee); // Some chains (eg Phi) have a gas price lower than 1Gwei.
|
||||
maxPriorityFeePerGas = fixGasPriceReturn(maxPriority); |
||||
baseFee = base; |
||||
} |
||||
*/ |
||||
public EIP1559FeeOracleResult getFeeOracleResult(BigInteger baseFee) |
||||
{ |
||||
return new EIP1559FeeOracleResult(getMaxFeePerGasWei(), getMaxPriorityFeePerGasWei(), baseFee); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,91 @@ |
||||
package com.alphawallet.app.ui.widget.entity; |
||||
|
||||
import android.os.Parcel; |
||||
import android.os.Parcelable; |
||||
|
||||
import com.alphawallet.app.entity.EIP1559FeeOracleResult; |
||||
import com.alphawallet.app.util.BalanceUtils; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.math.BigInteger; |
||||
|
||||
/** |
||||
* Created by JB on 20/01/2022. |
||||
*/ |
||||
public class GasSpeed implements Parcelable |
||||
{ |
||||
public final String speed; |
||||
public long seconds; |
||||
public final EIP1559FeeOracleResult gasPrice; |
||||
|
||||
public GasSpeed(String speed, long seconds, EIP1559FeeOracleResult gasPrice) |
||||
{ |
||||
this.speed = speed; |
||||
this.seconds = seconds; |
||||
this.gasPrice = gasPrice; |
||||
} |
||||
|
||||
public GasSpeed(Parcel in) |
||||
{ |
||||
speed = in.readString(); |
||||
seconds = in.readLong(); |
||||
gasPrice = in.readParcelable(EIP1559FeeOracleResult.class.getClassLoader()); |
||||
} |
||||
|
||||
public GasSpeed(String speed, long seconds, BigInteger gasPrice) |
||||
{ |
||||
this.speed = speed; |
||||
this.seconds = seconds; |
||||
this.gasPrice = new EIP1559FeeOracleResult(gasPrice, BigInteger.ZERO, BigInteger.ZERO); |
||||
} |
||||
|
||||
@Override |
||||
public int describeContents() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public void writeToParcel(Parcel dest, int flags) |
||||
{ |
||||
dest.writeString(speed); |
||||
dest.writeLong(seconds); |
||||
dest.writeParcelable(gasPrice, flags); |
||||
} |
||||
|
||||
public static final Creator<GasSpeed> CREATOR = new Creator<GasSpeed>() { |
||||
@Override |
||||
public GasSpeed createFromParcel(Parcel in) { |
||||
return new GasSpeed(in); |
||||
} |
||||
|
||||
@Override |
||||
public GasSpeed[] newArray(int size) { |
||||
return new GasSpeed[size]; |
||||
} |
||||
}; |
||||
|
||||
public BigDecimal calculateGasFee(BigDecimal useGasLimit, boolean isUsing1559) |
||||
{ |
||||
if (isUsing1559) |
||||
{ |
||||
return new BigDecimal(gasPrice.baseFee.add(gasPrice.priorityFee)).multiply(useGasLimit); |
||||
} |
||||
else |
||||
{ |
||||
return new BigDecimal(gasPrice.maxFeePerGas).multiply(useGasLimit); |
||||
} |
||||
} |
||||
|
||||
public BigDecimal calculateMaxGasFee(BigDecimal useGasLimit) |
||||
{ |
||||
if (gasPrice.maxFeePerGas != null && gasPrice.maxFeePerGas.compareTo(BigInteger.ZERO) > 0) |
||||
{ |
||||
return new BigDecimal(gasPrice.maxFeePerGas).multiply(useGasLimit); |
||||
} |
||||
else |
||||
{ |
||||
return BigDecimal.ZERO; |
||||
} |
||||
} |
||||
} |
@ -1,65 +0,0 @@ |
||||
package com.alphawallet.app.ui.widget.entity; |
||||
|
||||
import android.os.Parcel; |
||||
import android.os.Parcelable; |
||||
|
||||
import com.alphawallet.app.entity.EIP1559FeeOracleResult; |
||||
|
||||
import java.math.BigInteger; |
||||
|
||||
/** |
||||
* Created by JB on 20/01/2022. |
||||
*/ |
||||
public class GasSpeed2 implements Parcelable |
||||
{ |
||||
public final String speed; |
||||
public long seconds; |
||||
public final EIP1559FeeOracleResult gasPrice; |
||||
|
||||
public GasSpeed2(String speed, long seconds, EIP1559FeeOracleResult gasPrice) |
||||
{ |
||||
this.speed = speed; |
||||
this.seconds = seconds; |
||||
this.gasPrice = gasPrice; |
||||
} |
||||
|
||||
public GasSpeed2(Parcel in) |
||||
{ |
||||
speed = in.readString(); |
||||
seconds = in.readLong(); |
||||
gasPrice = in.readParcelable(EIP1559FeeOracleResult.class.getClassLoader()); |
||||
} |
||||
|
||||
public GasSpeed2(String speed, long seconds, BigInteger gasPrice) |
||||
{ |
||||
this.speed = speed; |
||||
this.seconds = seconds; |
||||
this.gasPrice = new EIP1559FeeOracleResult(gasPrice, BigInteger.ZERO, BigInteger.ZERO); |
||||
} |
||||
|
||||
@Override |
||||
public int describeContents() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public void writeToParcel(Parcel dest, int flags) |
||||
{ |
||||
dest.writeString(speed); |
||||
dest.writeLong(seconds); |
||||
dest.writeParcelable(gasPrice, flags); |
||||
} |
||||
|
||||
public static final Creator<GasSpeed2> CREATOR = new Creator<GasSpeed2>() { |
||||
@Override |
||||
public GasSpeed2 createFromParcel(Parcel in) { |
||||
return new GasSpeed2(in); |
||||
} |
||||
|
||||
@Override |
||||
public GasSpeed2[] newArray(int size) { |
||||
return new GasSpeed2[size]; |
||||
} |
||||
}; |
||||
} |
@ -1,126 +1,204 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:id="@+id/layout_list_item" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content"> |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:id="@+id/layout_list_item" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content"> |
||||
|
||||
<LinearLayout |
||||
android:id="@+id/layout_details" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_centerVertical="true" |
||||
android:orientation="vertical" |
||||
android:paddingStart="15dp" |
||||
android:paddingTop="15dp" |
||||
android:paddingBottom="15dp"> |
||||
android:id="@+id/layout_details" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_centerVertical="true" |
||||
android:orientation="vertical" |
||||
android:paddingStart="15dp" |
||||
android:paddingTop="15dp" |
||||
android:paddingBottom="15dp"> |
||||
|
||||
<LinearLayout |
||||
android:id="@+id/layout_speed_warning" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_weight="3.1" |
||||
android:orientation="horizontal" |
||||
android:visibility="gone" |
||||
tools:visibility="visible"> |
||||
android:id="@+id/layout_speed_warning" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="0dp" |
||||
android:layout_weight="3.1" |
||||
android:orientation="horizontal" |
||||
android:visibility="gone" |
||||
tools:visibility="visible"> |
||||
|
||||
<ImageView |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginEnd="6dp" |
||||
android:src="@drawable/ic_red_warning" /> |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginEnd="6dp" |
||||
android:src="@drawable/ic_red_warning" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_speed_warning" |
||||
style="@style/Aw.Typography.Title.SemiBold" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:text="@string/speed_too_low" |
||||
android:textColor="?colorError" /> |
||||
android:id="@+id/text_speed_warning" |
||||
style="@style/Aw.Typography.Title.SemiBold" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:text="@string/speed_too_low" |
||||
android:textColor="?colorError" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_speed" |
||||
style="@style/Aw.Typography.Title" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
tools:text="Average" /> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="4dp" |
||||
android:orientation="horizontal"> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_speed_cost" |
||||
style="@style/Aw.Typography.Caption.SemiBold" |
||||
android:id="@+id/text_speed" |
||||
style="@style/Aw.Typography.Title" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginEnd="3dp" |
||||
android:lineSpacingExtra="7sp" |
||||
tools:text="$0.55" /> |
||||
tools:text="Average" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_speed_cost_eth" |
||||
style="@style/Aw.Typography.Sub" |
||||
<LinearLayout |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
tools:text="(0.00362 ETH)" /> |
||||
android:layout_marginTop="4dp" |
||||
android:orientation="horizontal"> |
||||
|
||||
</LinearLayout> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal"> |
||||
<TextView |
||||
android:id="@+id/text_currency" |
||||
style="@style/Aw.Typography.Caption.SemiBold" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:lineSpacingExtra="7sp" |
||||
tools:text="$" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_gwei" |
||||
style="@style/Aw.Typography.Sub" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="3dp" |
||||
tools:text="Gas Price/Max Fee: 45" /> |
||||
android:id="@+id/text_speed_cost" |
||||
style="@style/Aw.Typography.Caption.SemiBold" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginEnd="3dp" |
||||
android:lineSpacingExtra="7sp" |
||||
tools:text="0.55" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_priority_fee" |
||||
style="@style/Aw.Typography.Sub" |
||||
android:layout_width="wrap_content" |
||||
android:id="@+id/text_speed_cost_eth" |
||||
style="@style/Aw.Typography.Sub" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
tools:text="0.00362ETH" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginStart="8dp" |
||||
android:layout_marginTop="3dp" |
||||
tools:text="Priority Fee: 2.00" /> |
||||
android:orientation="horizontal"> |
||||
|
||||
<LinearLayout |
||||
android:id="@+id/base_fee_layout" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:padding="3dp" |
||||
android:gravity="center"> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_base_fee" |
||||
style="@style/Aw.Typography.Sub" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
tools:text="Gas Price/Base Fee" |
||||
tools:visibility="visible" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_base_fee_value" |
||||
style="@style/Aw.Typography.Caption" |
||||
android:layout_marginStart="2dp" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
tools:text="12.03" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
<LinearLayout |
||||
android:id="@+id/max_fee_layout" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:padding="3dp" |
||||
android:visibility="gone" |
||||
tools:visibility="visible" |
||||
android:gravity="center"> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_max_fee" |
||||
style="@style/Aw.Typography.Sub" |
||||
android:text="@string/gas_max" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_max_fee_currency" |
||||
style="@style/Aw.Typography.Caption" |
||||
android:layout_marginStart="2dp" |
||||
tools:text="$" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_max_fee_value" |
||||
style="@style/Aw.Typography.Caption" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
tools:text="0.59" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
<LinearLayout |
||||
android:id="@+id/priority_fee_layout" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:padding="3dp" |
||||
android:visibility="gone" |
||||
tools:visibility="visible" |
||||
android:gravity="center"> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_priority_fee" |
||||
android:text="@string/priority_fee" |
||||
style="@style/Aw.Typography.Sub" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_priority_value" |
||||
style="@style/Aw.Typography.Caption" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginStart="2dp" |
||||
tools:text="0.18" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</LinearLayout> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_alignParentEnd="true" |
||||
android:layout_centerVertical="true" |
||||
android:orientation="horizontal" |
||||
android:paddingEnd="@dimen/tiny_8"> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_speed_time" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center_vertical" |
||||
android:layout_marginEnd="@dimen/tiny_8" |
||||
tools:text=" ≈ 2 minutes" /> |
||||
android:layout_alignParentEnd="true" |
||||
android:layout_centerVertical="true" |
||||
android:orientation="horizontal" |
||||
android:paddingEnd="@dimen/tiny_8"> |
||||
|
||||
<TextView |
||||
android:id="@+id/text_speed_time" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center_vertical" |
||||
android:layout_marginEnd="@dimen/tiny_8" |
||||
tools:text=" ≈ 2 minutes" /> |
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton |
||||
android:id="@+id/radio" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center_vertical" |
||||
android:clickable="false" |
||||
android:focusable="false" /> |
||||
android:id="@+id/radio" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center_vertical" |
||||
android:clickable="false" |
||||
android:focusable="false" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</RelativeLayout> |
||||
</RelativeLayout> |
||||
|
Loading…
Reference in new issue