Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Approval Dialogs #2324

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package com.alphawallet.app.ui;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
Expand All @@ -14,7 +10,6 @@
import com.alphawallet.app.web3.entity.WalletAddEthereumChainObject;
import com.alphawallet.app.widget.FunctionButtonBar;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;

import org.jetbrains.annotations.NotNull;

Expand Down
111 changes: 92 additions & 19 deletions app/src/main/java/com/alphawallet/app/ui/DappBrowserFragment.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.alphawallet.app.ui;

import static com.alphawallet.app.C.DEFAULT_GAS_LIMIT_FOR_NONFUNGIBLE_TOKENS;
import static com.alphawallet.app.C.ETHER_DECIMALS;
import static com.alphawallet.app.C.RESET_TOOLBAR;
import static com.alphawallet.app.entity.CryptoFunctions.sigFromByteArray;
import static com.alphawallet.app.entity.Operation.SIGN_DATA;
import static com.alphawallet.app.entity.WalletPage.DAPP_BROWSER;
import static com.alphawallet.app.entity.tokens.Token.TOKEN_BALANCE_PRECISION;
import static com.alphawallet.app.ui.HomeActivity.RESET_TOKEN_SERVICE;
import static com.alphawallet.app.ui.MyAddressActivity.KEY_ADDRESS;
Expand Down Expand Up @@ -112,6 +110,7 @@
import com.alphawallet.app.web3.OnSignPersonalMessageListener;
import com.alphawallet.app.web3.OnSignTransactionListener;
import com.alphawallet.app.web3.OnSignTypedMessageListener;
import com.alphawallet.app.web3.OnWalletActionListener;
import com.alphawallet.app.web3.OnWalletAddEthereumChainObjectListener;
import com.alphawallet.app.web3.Web3View;
import com.alphawallet.app.web3.entity.Address;
Expand All @@ -135,7 +134,6 @@
import org.web3j.crypto.Sign;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthEstimateGas;

import java.io.ByteArrayOutputStream;
import java.io.File;
Expand All @@ -162,8 +160,8 @@

public class DappBrowserFragment extends BaseFragment implements OnSignTransactionListener, OnSignPersonalMessageListener,
OnSignTypedMessageListener, OnSignMessageListener, OnEthCallListener, OnWalletAddEthereumChainObjectListener,
URLLoadInterface, ItemClickListener, OnDappHomeNavClickListener, DappBrowserSwipeInterface, SignAuthenticationCallback,
ActionSheetCallback, TestNetDialog.TestNetDialogCallback
OnWalletActionListener, URLLoadInterface, ItemClickListener, OnDappHomeNavClickListener, DappBrowserSwipeInterface,
SignAuthenticationCallback, ActionSheetCallback, TestNetDialog.TestNetDialogCallback
{
private static final String TAG = DappBrowserFragment.class.getSimpleName();
private static final String DAPP_BROWSER = "DAPP_BROWSER";
Expand Down Expand Up @@ -1035,6 +1033,7 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) {
web3.setOnSignTypedMessageListener(this);
web3.setOnEthCallListener(this);
web3.setOnWalletAddEthereumChainObjectListener(this);
web3.setOnWalletActionListener(this);

if (loadOnInit != null)
{
Expand Down Expand Up @@ -1156,7 +1155,7 @@ public void onEthCall(Web3Call call)
}

@Override
public void onWalletAddEthereumChainObject(WalletAddEthereumChainObject chainObj)
public void onWalletAddEthereumChainObject(long callbackId, WalletAddEthereumChainObject chainObj)
{
// read chain value
long chainId = chainObj.getChainId();
Expand All @@ -1176,11 +1175,21 @@ public void onWalletAddEthereumChainObject(WalletAddEthereumChainObject chainObj
addCustomChainDialog.dismiss();
});
addCustomChainDialog.show();
return;
}
else
{
changeChainRequest(callbackId, info);
}
}

private void changeChainRequest(long callbackId, NetworkInfo info)
{
//Don't show dialog if network doesn't need to be changed or if already showing
if ((activeNetwork != null && activeNetwork.chainId == chainId) || (chainSwapDialog != null && chainSwapDialog.isShowing())) return;
if ((activeNetwork != null && activeNetwork.chainId == info.chainId) || (chainSwapDialog != null && chainSwapDialog.isShowing()))
{
web3.onWalletActionSuccessful(callbackId, null);
return;
}

//if we're switching between mainnet and testnet we need to pop open the 'switch to testnet' dialog (class TestNetDialog)
// - after the user switches to testnet, go straight to switching the network (loadNewNetwork)
Expand All @@ -1195,26 +1204,76 @@ public void onWalletAddEthereumChainObject(WalletAddEthereumChainObject chainObj
else
{
//go straight to chain change dialog
showChainChangeDialog(info);
showChainChangeDialog(callbackId, info);
}
}

private void showChainChangeDialog(NetworkInfo newNetwork)
@Override
public void onRequestAccounts(long callbackId)
{
//TODO: Pop open dialog which asks user to confirm they wish to expose their address to this dapp eg:
//title = "Request Account Address"
//message = "${dappUrl} requests your address. \nAuthorise?"
//if user authorises, then do an evaluateJavascript to populate the web3.eth.getCoinbase with the current address,
//and additionally add a window.ethereum.setAddress function in init.js to set up addresses
//together with this update, also need to track which websites have been given permission, and if they already have it (can probably get away with using SharedPrefs)
//then automatically perform with step without a dialog (ie same as it does currently)
web3.onWalletActionSuccessful(callbackId, "[" + wallet.address + "]");
}

//EIP-3326
@Override
public void onWalletSwitchEthereumChain(long callbackId, WalletAddEthereumChainObject chainObj)
{
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.request_change_chain, newNetwork.name, String.valueOf(newNetwork.chainId)))
.setPositiveButton(R.string.dialog_ok, (d, w) -> loadNewNetwork(newNetwork.chainId))
.setNegativeButton(R.string.action_cancel, (d, w) -> chainSwapDialog.dismiss())
.setCancelable(true);
//request user to change chains
long chainId = chainObj.getChainId();
final NetworkInfo info = viewModel.getNetworkInfo(chainId);
if (info == null)
{
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
.setTitle(R.string.unknown_network_title)
.setMessage(getString(R.string.unknown_network, String.valueOf(chainId)))
.setPositiveButton(R.string.dialog_ok, (d, w) -> {
if (chainSwapDialog != null && chainSwapDialog.isShowing()) chainSwapDialog.dismiss();
})
.setCancelable(true);

chainSwapDialog = builder.create();
chainSwapDialog.show();
}
else
{
changeChainRequest(callbackId, info);
}
}

//Warn if we're switching between network types
/**
*
* This will pop the ActionSheetDialog to request a chain change, with appropriate warning
* if switching between mainnets and testnets
*
* @param callbackId
* @param newNetwork
*/
private void showChainChangeDialog(long callbackId, NetworkInfo newNetwork)
{
Token baseToken = viewModel.getTokenService().getTokenOrBase(newNetwork.chainId, wallet.address);
String message = getString(R.string.request_change_chain, newNetwork.name, String.valueOf(newNetwork.chainId));
if (newNetwork.hasRealValue() && !activeNetwork.hasRealValue())
{
builder.setTitle(R.string.warning_switch_to_main);
message += "\n" + getString(R.string.warning_switch_to_main);
}
else if (!newNetwork.hasRealValue() && activeNetwork.hasRealValue())
{
message += "\n" + getString(R.string.warning_switching_to_test);
}

chainSwapDialog = builder.create();
chainSwapDialog.show();
confirmationDialog = new ActionSheetDialog(getActivity(), this, R.string.switch_chain_request, message, R.string.switch_and_reload,
callbackId, baseToken);

confirmationDialog.setCanceledOnTouchOutside(true);
confirmationDialog.show();
confirmationDialog.fullExpand();
}

private void handleSignMessage(Signable message)
Expand Down Expand Up @@ -2001,6 +2060,20 @@ public void pinAuthorisation(boolean gotAuth)
}
}

@Override
public void buttonClick(long callbackId, Token baseToken)
{
//handle button click
if (confirmationDialog != null && confirmationDialog.isShowing())
{
confirmationDialog.dismiss();
}

//switch network
loadNewNetwork(baseToken.tokenInfo.chainId);
web3.onWalletActionSuccessful(callbackId, null);
}

@Override
public void cancelAuthentication()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.app.Activity;

import com.alphawallet.app.entity.SignAuthenticationCallback;
import com.alphawallet.app.entity.tokens.Token;
import com.alphawallet.app.web3.entity.Web3Transaction;

/**
Expand All @@ -15,4 +16,6 @@ public interface ActionSheetCallback
void dismissed(String txHash, long callbackId, boolean actionCompleted);
void notifyConfirm(String mode);
default void signTransaction(Web3Transaction tx) { } // only WalletConnect uses this so far

default void buttonClick(long callbackId, Token baseToken) { }; //for message only actionsheet
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ public class JsInjectorClient {
private final Context context;
private final OkHttpClient httpClient;

private String jsLibrary;

private long chainId = 1;
private Address walletAddress;
//Note: this default RPC is overriden before injection
Expand Down Expand Up @@ -73,52 +71,14 @@ public void setRpcUrl(String rpcUrl) {
this.rpcUrl = rpcUrl;
}

@Nullable
JsInjectorResponse loadUrl(final String url, final Map<String, String> headers) {
Request request = buildRequest(url, headers);
JsInjectorResponse result = null;
try {
Response response = httpClient.newCall(request).execute();
result = buildResponse(response);
} catch (Exception ex) {
Log.d("REQUEST_ERROR", "", ex);
}
return result;
}

String assembleJs(Context context, String template) {
if (TextUtils.isEmpty(jsLibrary)) {
jsLibrary = loadFile(context, R.raw.alphawallet_min);
}
String initJs = loadInitJs(context);
return String.format(template, jsLibrary, initJs);
}

@Nullable
private JsInjectorResponse buildResponse(Response response) {
String body = null;
int code = response.code();
try {
if (response.isSuccessful()) {
body = response.body().string();
}
} catch (IOException ex) {
Log.d("READ_BODY_ERROR", "Ex", ex);
}
Request request = response.request();
Response prior = response.priorResponse();
boolean isRedirect = prior != null && prior.isRedirect();
String result = injectJS(body);
String contentType = getContentTypeHeader(response);
String charset = getCharset(contentType);
String mime = getMimeType(contentType);
String finalUrl = request.url().toString();
return new JsInjectorResponse(result, code, finalUrl, mime, charset, isRedirect);
public String initJs(Context context)
{
return loadInitJs(context);
}

String injectJS(String html) {
String js = assembleJs(context, JS_TAG_TEMPLATE);
return injectJS(html, js);
public String providerJs(Context context)
{
return loadFile(context, R.raw.alphawallet_min);
}

String injectWeb3TokenInit(Context ctx, String view, String tokenContent, BigInteger tokenId)
Expand All @@ -128,7 +88,7 @@ String injectWeb3TokenInit(Context ctx, String view, String tokenContent, BigInt
String tokenIdWrapperName = "token-card-" + tokenId.toString(10);
initSrc = String.format(initSrc, tokenContent, walletAddress, EthereumNetworkRepository.getDefaultNodeURL(chainId), chainId, tokenIdWrapperName);
//now insert this source into the view
// note that the <div> is not closed because it is closed in njectStyleAndWrap().
// note that the <div> is not closed because it is closed in injectStyleAndWrap().
String wrapper = "<div id=\"token-card-" + tokenId.toString(10) + "\" class=\"token-card\">";
initSrc = "<script>\n" + initSrc + "</script>\n" + wrapper;
return injectJS(view, initSrc);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.alphawallet.app.web3;

import com.alphawallet.app.web3.entity.WalletAddEthereumChainObject;

/**
* Created by JB on 15/01/2022.
*/
public interface OnWalletActionListener
{
void onRequestAccounts(long callbackId);
void onWalletSwitchEthereumChain(long callbackId, WalletAddEthereumChainObject chainObj);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

public interface OnWalletAddEthereumChainObjectListener
{
void onWalletAddEthereumChainObject(WalletAddEthereumChainObject chainObject);
void onWalletAddEthereumChainObject(long callbackId, WalletAddEthereumChainObject chainObject);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public class SignCallbackJSInterface
private final OnEthCallListener onEthCallListener;
@NonNull
private final OnWalletAddEthereumChainObjectListener onWalletAddEthereumChainObjectListener;
@NonNull
private final OnWalletActionListener onWalletActionListener;

public SignCallbackJSInterface(
WebView webView,
Expand All @@ -50,14 +52,16 @@ public SignCallbackJSInterface(
@NonNull OnSignPersonalMessageListener onSignPersonalMessageListener,
@NonNull OnSignTypedMessageListener onSignTypedMessageListener,
@NotNull OnEthCallListener onEthCallListener,
@NonNull OnWalletAddEthereumChainObjectListener onWalletAddEthereumChainObjectListener) {
@NonNull OnWalletAddEthereumChainObjectListener onWalletAddEthereumChainObjectListener,
@NonNull OnWalletActionListener onWalletActionListener) {
this.webView = webView;
this.onSignTransactionListener = onSignTransactionListener;
this.onSignMessageListener = onSignMessageListener;
this.onSignPersonalMessageListener = onSignPersonalMessageListener;
this.onSignTypedMessageListener = onSignTypedMessageListener;
this.onEthCallListener = onEthCallListener;
this.onWalletAddEthereumChainObjectListener = onWalletAddEthereumChainObjectListener;
this.onWalletActionListener = onWalletActionListener;
}

@JavascriptInterface
Expand Down Expand Up @@ -94,6 +98,11 @@ public void signPersonalMessage(int callbackId, String data) {
webView.post(() -> onSignPersonalMessageListener.onSignPersonalMessage(new EthereumMessage(data, getUrl(), callbackId, SignMessageType.SIGN_PERSONAL_MESSAGE)));
}

@JavascriptInterface
public void requestAccounts(long callbackId) {
webView.post(() -> onWalletActionListener.onRequestAccounts(callbackId) );
}

@JavascriptInterface
public void signTypedMessage(int callbackId, String data) {
webView.post(() -> {
Expand Down Expand Up @@ -140,7 +149,23 @@ public void walletAddEthereumChain(int callbackId, String msgParams) {
WalletAddEthereumChainObject chainObj = new Gson().fromJson(msgParams, WalletAddEthereumChainObject.class);
if (!TextUtils.isEmpty(chainObj.chainId))
{
webView.post(() -> onWalletAddEthereumChainObjectListener.onWalletAddEthereumChainObject(chainObj));
webView.post(() -> onWalletAddEthereumChainObjectListener.onWalletAddEthereumChainObject(callbackId, chainObj));
}
}
catch (JsonSyntaxException e)
{
if (BuildConfig.DEBUG) e.printStackTrace();
}
}

@JavascriptInterface
public void walletSwitchEthereumChain(int callbackId, String msgParams) {
try
{ //{"chainId":"0x89","chainType":"ETH"}
WalletAddEthereumChainObject chainObj = new Gson().fromJson(msgParams, WalletAddEthereumChainObject.class);
if (!TextUtils.isEmpty(chainObj.chainId))
{
webView.post(() -> onWalletActionListener.onWalletSwitchEthereumChain(callbackId, chainObj));// onWalletAddEthereumChainObjectListener.onWalletAddEthereumChainObject(chainObj));
}
}
catch (JsonSyntaxException e)
Expand Down
Loading