Skip to content

inject invalid #740

@lxchao1024

Description

@lxchao1024

当我加载uniswap的时候,参数如下:

private const val DAPP_URL = "https://app.uniswap.org"
private const val CHAIN_ID = 1
private const val RPC_URL = "https://eth.llamarpc.com"

加载后,发现无法进行交互:
Image

下面是我的代码片段:

`package com.wallet.view

import android.app.Activity.RESULT_OK
import android.app.AlertDialog
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.net.http.SslError
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.*
import android.widget.ProgressBar
import androidx.fragment.app.Fragment
import com.io.wallet.R
import com.wallet.bean.ETHWallet
import com.wallet.manager.WalletManager
import com.wallet.web3.provider.WebAppCallback
import com.wallet.web3.provider.WebAppInterface
import com.wallet.web3.provider.emitChainChanged

open class DappBrowserFragment : Fragment() {

private val TAG = "DappBrowserFragment"
private var url = ""
private var chainId = "1"
private var rpcUrl = ""
private var web3Browser: WebView? = null
private var webAppInterface: WebAppInterface? = null
private var progressBar: ProgressBar? = null
private var listener: WebAppCallback? = null

companion object {
    fun newInstance(url: String, chainId: String, rpcUrl: String): DappBrowserFragment {
        val args = Bundle()
        args.putString("url", url)
        args.putString("chainId", chainId)
        args.putString("rpcUrl", rpcUrl)
        val fragment = DappBrowserFragment()
        fragment.arguments = args
        return fragment
    }
}

fun getWebAppInterface(): WebAppInterface? {
    return webAppInterface
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
}

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val view: View = inflater.inflate(R.layout.fragment_dapp_browser, container, false)
    arguments?.let {
        url = it.getString("url", "")
        chainId = it.getString("chainId", WalletManager.getDefaultBrowserNet()?.chainId ?: "1")
        rpcUrl =
            it.getString("rpcUrl", WalletManager.getDefaultBrowserNet()?.getChainRpcUrl() ?: "")

// rpcUrl = "https://polygon-rpc.com"
}
initView(view)
return view
}

protected fun initView(view: View) {
    progressBar = view.findViewById(R.id.progressBar)
    web3Browser = view.findViewById(R.id.webView)
    val webSettings: WebSettings? = web3Browser?.settings
    val iOSUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1"
    webSettings?.let {
        webSettings.javaScriptEnabled = true

// webSettings.setCacheMode(WebSettings.LOAD_DEFAULT)
webSettings.builtInZoomControls = true
webSettings.displayZoomControls = false
webSettings.useWideViewPort = true
webSettings.loadWithOverviewMode = true
webSettings.domStorageEnabled = true
// webSettings.userAgentString = iOSUserAgent
}
initData()
listener?.afterLoadData()
}

private fun initData() {
    val providerJs = loadProviderJs()
    val initJs = loadInitJs(chainId, rpcUrl)
    Log.e(TAG, "providerJs: $providerJs")
    Log.e(TAG, "initJs: $initJs")
    web3Browser?.let {
        if (null != activity) {
            webAppInterface = WebAppInterface(requireActivity(), it, url, rpcUrl, listener)
            webAppInterface?.run {
                it.addJavascriptInterface(this, "_tw_")
                val webViewClient = object : WebViewClient() {
                    override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                        super.onPageStarted(view, url, favicon)//设置trust_min.js
                        //设置trust_min.js
                        web3Browser?.evaluateJavascript(providerJs) {
                            Log.e(TAG, "evaluateJavascript, providerJs: $it")
                        }
                        //设置js初始化语句
                        Log.e(TAG, "初始化脚本 onPageStarted")
                        web3Browser?.evaluateJavascript(initJs) {
                            Log.e(TAG, "evaluateJavascript, initJs: $it")
                        }
                    }

                    override fun onReceivedSslError(
                        view: WebView?,
                        handler: SslErrorHandler?,
                        error: SslError?
                    ) {
                        if (activity != null) {
                            AlertDialog.Builder(activity)
                                .setTitle("Trust")
                                .setMessage("Please trust the certification")
                                .setNegativeButton("No") { _, _ ->
                                    handler?.cancel()
                                }.setPositiveButton("Trust") { _, _ ->
                                    handler?.proceed()
                                }
                                .create().show()
                        }
                    }

                    override fun onPageFinished(view: WebView?, url: String?) {
                        super.onPageFinished(view, url)
                        Log.e(TAG, "onPageFinished(), url: $url")
                        listener?.onPageFinished(url)
                    }
                }
                it.webChromeClient = object : WebChromeClient() {
                    override fun onProgressChanged(view: WebView?, newProgress: Int) {
                        if (newProgress == 100) {
                            progressBar?.visibility = View.GONE
                        } else {
                            progressBar?.visibility = View.VISIBLE
                            progressBar?.progress = newProgress
                        }
                    }

                    override fun onShowFileChooser(
                        webView: WebView?,
                        filePathCallback: ValueCallback<Array<Uri>>?,
                        fileChooserParams: FileChooserParams?
                    ): Boolean {
                        Log.e(TAG, "onShowFileChooser=====")
                        uploadMessageAboveL = filePathCallback
                        chooseAlbumPic()
                        return true
                    }

                    override fun onReceivedTitle(view: WebView?, title: String?) {
                        super.onReceivedTitle(view, title)
                        //title

// webAppInterface?.setTitle(title ?: "")
}

                    override fun onReceivedIcon(view: WebView?, icon: Bitmap?) {
                        super.onReceivedIcon(view, icon)
                        //icon
                        webAppInterface?.setBitmap(icon)
                    }
                }
                it.webViewClient = webViewClient
                it.loadUrl(url)
            }
        }
    }
}

private fun loadProviderJs(): String {
    return resources.openRawResource(R.raw.trust_min).bufferedReader().use { it.readText() }
}

private fun loadInitJs(chainId: String, rpcUrl: String): String {
    Log.e(TAG, "loadInitJs, chainId: $chainId, rpcUrl: $rpcUrl")
    val source = """
(function() {
    console.log("初始化Trust Wallet Provider...");
    var config = {                
        ethereum: {
            chainId: $chainId,
            rpcUrl: "$rpcUrl"
        },
        solana: {
            cluster: "mainnet-beta"
        },
        isDebug: true
    };
    
    // 确保trustwallet对象存在
    if (typeof trustwallet === 'undefined') {
        console.error('trustwallet对象未定义!');
        return;
    }
    
    console.log("创建Provider...");
    trustwallet.ethereum = new trustwallet.Provider(config);
    trustwallet.solana = new trustwallet.SolanaProvider(config);
    trustwallet.cosmos = new trustwallet.CosmosProvider(config);
    trustwallet.aptos = new trustwallet.AptosProvider(config);
    trustwallet.isMetaMask = true;
    
    trustwallet.postMessage = (json) => {
        console.log("发送消息到原生:", json);
        window._tw_.postMessage(JSON.stringify(json));
    }
    
    console.log("设置window.ethereum...");
    window.ethereum = trustwallet.ethereum;
    window.keplr = trustwallet.cosmos;
    window.aptos = trustwallet.aptos;
    
    // 如果需要,也可以设置window.solana
    if (typeof window.solana === 'undefined') {
        window.solana = trustwallet.solana;
    }
    
    console.log("初始化完成");
    const getDefaultCosmosProvider = (chainId) => {
        return trustwallet.cosmos.getOfflineSigner(chainId);
    }
    window.getOfflineSigner = getDefaultCosmosProvider;
    window.getOfflineSignerOnlyAmino = getDefaultCosmosProvider;
    window.getOfflineSignerAuto = getDefaultCosmosProvider;
})();
    (function() {
        window.ethereum.isMetaMask = true;
    })()
// MetaMask 兼容性
    (function() {
        if (window.ethereum) {
            Object.defineProperty(window.ethereum, 'isMetaMask', {
                value: true,
                writable: false,
                configurable: false
            });
        }
    })();
"""
    return source
}

fun reload(chainId: String, rpcUrl: String) {
    this.chainId = chainId
    this.rpcUrl = rpcUrl
    initData()
}

fun reloadData(chainId: String, rpcUrl: String) {
    this.chainId = chainId
    this.rpcUrl = rpcUrl
}

fun changeAddress() {
    webAppInterface?.setProvider2(chainId, rpcUrl)
    web3Browser?.emitChainChanged(chainId)
}

fun setAddress(address: String) {
    if (address.isNullOrEmpty()) return
    val setAddress = "window.ethereum.setAddress(\"$address\");"
    web3Browser?.post {

// Log.e("aaa", "setAddress::$setAddress")
web3Browser?.evaluateJavascript(setAddress, null)
}
}

fun canBack(): Boolean = web3Browser?.canGoBack() ?: false

fun goBack() {
    web3Browser?.goBack()
}

fun setListener(l: WebAppCallback?) {
    this.listener = l
    webAppInterface?.setListener(l)
    Log.e(TAG, "null? === ${null == webAppInterface}")
}

private val REQUEST_CODE_ALBUM = 0x0111
private var uploadMessageAboveL: ValueCallback<Array<Uri>>? = null

/**
 * 选择相册照片
 */
private fun chooseAlbumPic() {
    val i = Intent(Intent.ACTION_GET_CONTENT)
    i.addCategory(Intent.CATEGORY_OPENABLE)
    i.type = "image/*"
    startActivityForResult(Intent.createChooser(i, "Image Chooser"), REQUEST_CODE_ALBUM)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_CODE_ALBUM) {
        uploadMessageAboveL?.let { callback ->
            if (resultCode == RESULT_OK) {
                data?.data?.let {
                    callback.onReceiveValue(arrayOf(it))
                }
            } else {
                callback.onReceiveValue(null)
            }
            uploadMessageAboveL = null
        }
    }
}

}`

package com.trust.web3.demo

import android.content.Context
import android.util.Log
import android.webkit.JavascriptInterface
import android.webkit.WebView
import org.json.JSONObject
import splitties.alertdialog.appcompat.cancelButton
import splitties.alertdialog.appcompat.message
import splitties.alertdialog.appcompat.okButton
import splitties.alertdialog.appcompat.title
import splitties.alertdialog.material.materialAlertDialog
import wallet.core.jni.CoinType
import wallet.core.jni.Curve
import wallet.core.jni.PrivateKey

class WebAppInterface(
private val context: Context,
private val webView: WebView,
private val dappUrl: String
) {
private val privateKey =
PrivateKey("0x4646464646464646464646464646464646464646464646464646464646464646".toHexByteArray())
private val addr = CoinType.ETHEREUM.deriveAddress(privateKey).toLowerCase()
private val pubKey = CoinType.SOLANA.deriveAddress(privateKey)

@JavascriptInterface
fun postMessage(json: String) {
    val obj = JSONObject(json)
    println(obj)
    val id = obj.getLong("id")
    val method = DAppMethod.fromValue(obj.getString("name"))
    val network = obj.getString("network")
    Log.e("WebAppInterface", "Received message: id=$id, method=$method, network=$network, obj: $obj")
    when (method) {
        DAppMethod.REQUESTACCOUNTS -> {
            context.materialAlertDialog {
                title = "Request Accounts"
                message = "${dappUrl} requests your address"
                okButton {
                    val address = if (network == "solana") pubKey else addr
                    val setAddress = "window.$network.setAddress(\"$address\");"
                    val callback = "window.$network.sendResponse($id, [\"$address\"])"
                    webView.post {
                        webView.evaluateJavascript(setAddress) {
                            // ignore
                        }
                        webView.evaluateJavascript(callback) { value ->
                            println(value)
                        }
                    }
                }
                cancelButton()
            }.show()
        }
        DAppMethod.SIGNMESSAGE -> {
            val data = extractMessage(obj)
            if (network == "ethereum")
                handleSignMessage(id, data, addPrefix = false)
            else
                handleSignSolanaMessage(id, data)
        }
        DAppMethod.SIGNPERSONALMESSAGE -> {
            val data = extractMessage(obj)
            handleSignMessage(id, data, addPrefix = true)
        }
        DAppMethod.SIGNTYPEDMESSAGE -> {
            val data = extractMessage(obj)
            val raw = extractRaw(obj)
            handleSignTypedMessage(id, data, raw)
        }
        else -> {
            context.materialAlertDialog {
                title = "Error"
                message = "$method not implemented"
                okButton {
                }
            }.show()
        }
    }
}

private fun extractMessage(json: JSONObject): ByteArray {
    val param = json.getJSONObject("object")
    val data = param.getString("data")
    return Numeric.hexStringToByteArray(data)
}

private fun extractRaw(json: JSONObject): String {
    val param = json.getJSONObject("object")
    return param.getString("raw")
}

private fun handleSignMessage(id: Long, data: ByteArray, addPrefix: Boolean) {
    context.materialAlertDialog {
        title = "Sign Ethereum Message"
        message = if (addPrefix) String(data, Charsets.UTF_8) else Numeric.toHexString(data)
        cancelButton {
            webView.sendError("ethereum","Cancel", id)
        }
        okButton {
            webView.sendResult("ethereum", signEthereumMessage(data, addPrefix), id)
        }
    }.show()
}

private fun handleSignSolanaMessage(id: Long, data: ByteArray) {
    context.materialAlertDialog {
        title = "Sign Solana Message"
        message = String(data, Charsets.UTF_8) ?: Numeric.toHexString(data)
        cancelButton {
            webView.sendError("solana", "Cancel", id)
        }
        okButton {
            webView.sendResult("solana", signSolanaMessage(data), id)
        }
    }.show()
}

private fun handleSignTypedMessage(id: Long, data: ByteArray, raw: String) {
    context.materialAlertDialog {
        title = "Sign Typed Message"
        message = raw
        cancelButton {
            webView.sendError("ethereum","Cancel", id)
        }
        okButton {
            webView.sendResult("ethereum", signEthereumMessage(data, false), id)
        }
    }.show()
}

private fun signEthereumMessage(message: ByteArray, addPrefix: Boolean): String {
    var data = message
    if (addPrefix) {
        val messagePrefix = "\u0019Ethereum Signed Message:\n"
        val prefix = (messagePrefix + message.size).toByteArray()
        val result = ByteArray(prefix.size + message.size)
        System.arraycopy(prefix, 0, result, 0, prefix.size)
        System.arraycopy(message, 0, result, prefix.size, message.size)
        data = wallet.core.jni.Hash.keccak256(result)
    }

    val signatureData = privateKey.sign(data, Curve.SECP256K1)
        .apply {
            (this[this.size - 1]) = (this[this.size - 1] + 27).toByte()
        }
    return Numeric.toHexString(signatureData)
}

private fun signSolanaMessage(message: ByteArray): String {
    val signature = privateKey.sign(message, Curve.ED25519)
    return Numeric.toHexString(signature)

}

}

谁能告诉我该如何才能进行交互

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions