diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 00000000..59bdbd64 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "com.trust.web3.demo" + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.2.0' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation project(path: ':lib') + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' +} \ No newline at end of file diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..837b9dcb --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/java/com/trust/web3/demo/MainActivity.kt b/android/app/src/main/java/com/trust/web3/demo/MainActivity.kt new file mode 100644 index 00000000..6045b06c --- /dev/null +++ b/android/app/src/main/java/com/trust/web3/demo/MainActivity.kt @@ -0,0 +1,57 @@ +package com.trust.web3.demo + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.webkit.WebView +import android.webkit.WebViewClient + +class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + val provderJs = loadProviderJs() + val initJs = loadInitJs( + 1, + "https://mainnet.infura.io/v3/6e822818ec644335be6f0ed231f48310" + ) + println("file lenght: ${provderJs.length}") + WebView.setWebContentsDebuggingEnabled(true) + val webview: WebView = findViewById(R.id.webview) + webview.settings.javaScriptEnabled = true + webview.addJavascriptInterface(WebAppInterface(webview), "_tw_") + + val webViewClient = object : WebViewClient() { + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + println("loaded: ${url}") + view?.evaluateJavascript(provderJs, null) + view?.evaluateJavascript(initJs, null) + } + } + webview.webViewClient = webViewClient + webview.loadUrl("https://js-eth-sign.surge.sh") + } + + fun loadProviderJs(): String { + return resources.openRawResource(R.raw.trust).bufferedReader().use { it.readText() } + } + + fun loadInitJs(chainId: Int, rpcUrl: String): String { + val source = """ + (function() { + var config = { + chainId: $chainId, + rpcUrl: "$rpcUrl", + isDebug: true + }; + window.ethereum = new trustwallet.Provider(config); + window.web3 = new trustwallet.Web3(window.ethereum); + trustwallet.postMessage = (json) => { + window._tw_.postMessage(JSON.stringify(json)); + } + })(); + """ + return source + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/trust/web3/demo/WebAppInterface.kt b/android/app/src/main/java/com/trust/web3/demo/WebAppInterface.kt new file mode 100644 index 00000000..84c2a859 --- /dev/null +++ b/android/app/src/main/java/com/trust/web3/demo/WebAppInterface.kt @@ -0,0 +1,30 @@ +package com.trust.web3.demo + +import android.content.Context +import android.webkit.JavascriptInterface +import android.webkit.WebView +import android.widget.Toast +import org.json.JSONObject + +class WebAppInterface(private val context: WebView) { + @JavascriptInterface + fun postMessage(json: String) { + val obj = JSONObject(json) + println(obj) + val id = obj["id"] + val addr = "0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1" + + when(obj["name"]) { + "requestAccounts" -> { + val callback = "window.ethereum.sendResponse($id, [\"$addr\"])" + context.post { + context.evaluateJavascript(callback) { value -> + println(value) + } + } + } + // handle other methods here + // signTransaction, signMessage, ecRecover, watchAsset, addEthereumChain + } + } +} diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..5c9c82fd --- /dev/null +++ b/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..f8c6127d --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..2112ca5a --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Web3 Provider Demo + \ No newline at end of file diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..eebc2d04 --- /dev/null +++ b/android/app/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 2c69ff2e..691b4272 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,17 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. - buildscript { - + ext.kotlin_version = "1.4.31" repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.0' + classpath "com.android.tools.build:gradle:4.1.2" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files } } diff --git a/android/gradle.properties b/android/gradle.properties index 743d692c..98bed167 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -6,8 +6,16 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 4b62c662..3c1f772e 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Sep 15 09:48:52 CST 2020 +#Wed Mar 17 17:43:08 JST 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/android/settings.gradle b/android/settings.gradle index 8c2a2e0f..45be8697 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1,3 @@ +include ':app' include ':lib' +rootProject.name = "Web3 Provider Demo" \ No newline at end of file diff --git a/dist/trust-min.js b/dist/trust-min.js index 240dc101..2fedc7bd 100644 --- a/dist/trust-min.js +++ b/dist/trust-min.js @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:122b36451d32ff5076cbfa594e8234da049735ed5f9e1ed59d3a066895afb87c -size 535632 +oid sha256:f125c6e2ea85c391af8834fbdc74817bec2275256b2208fbb05e9ce2a43409d9 +size 535775 diff --git a/ios/TrustWeb3Provider/DAppWebViewController.swift b/ios/TrustWeb3Provider/DAppWebViewController.swift index 1eb327ff..e3edf856 100644 --- a/ios/TrustWeb3Provider/DAppWebViewController.swift +++ b/ios/TrustWeb3Provider/DAppWebViewController.swift @@ -22,8 +22,7 @@ class DAppWebViewController: UIViewController { return WKUserScriptConfig( address: address, chainId: 1, - rpcUrl: "https://mainnet.infura.io/v3/6e822818ec644335be6f0ed231f48310", - privacyMode: false + rpcUrl: "https://mainnet.infura.io/v3/6e822818ec644335be6f0ed231f48310" ) }() @@ -37,9 +36,7 @@ class DAppWebViewController: UIViewController { let controller = WKUserContentController() controller.addUserScript(scriptConfig.providerScript) controller.addUserScript(scriptConfig.injectedScript) - for name in DAppMethod.allCases { - controller.add(self, name: name.rawValue) - } + controller.add(self, name: "_tw_") config.userContentController = controller diff --git a/ios/TrustWeb3Provider/WKUserScriptConfig.swift b/ios/TrustWeb3Provider/WKUserScriptConfig.swift index 82b5121c..84904d37 100644 --- a/ios/TrustWeb3Provider/WKUserScriptConfig.swift +++ b/ios/TrustWeb3Provider/WKUserScriptConfig.swift @@ -12,7 +12,6 @@ struct WKUserScriptConfig { let address: String let chainId: Int let rpcUrl: String - let privacyMode: Bool var providerJsBundleUrl: URL { let bundlePath = Bundle.main.path(forResource: "TrustWeb3Provider", ofType: "bundle") @@ -31,38 +30,22 @@ struct WKUserScriptConfig { } var injectedScript: WKUserScript { - let source: String - if privacyMode { - source = """ - (function() { - var config = { - chainId: \(chainId), - rpcUrl: "\(rpcUrl)" - }; - const provider = new window.Trust(config); - window.ethereum = provider; + let source = + """ + (function() { + var config = { + chainId: \(chainId), + rpcUrl: "\(rpcUrl)", + isDebug: true + }; + window.ethereum = new trustwallet.Provider(config); + window.web3 = new trustwallet.Web3(window.ethereum); + trustwallet.postMessage = (jsonString) => { + webkit.messageHandlers._tw_.postMessage(jsonString) + }; + })(); + """ - window.chrome = {webstore: {}}; - })(); - """ - } else { - source = """ - (function() { - var config = { - address: "\(address)".toLowerCase(), - chainId: \(chainId), - rpcUrl: "\(rpcUrl)" - }; - const provider = new window.Trust(config); - provider.isDebug = true; - window.ethereum = provider; - window.web3 = new window.Web3(provider); - window.web3.eth.defaultAccount = config.address; - - window.chrome = {webstore: {}}; - })(); - """ - } let script = WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false) return script } diff --git a/src/index.js b/src/index.js index 6417ef86..1798a4b7 100644 --- a/src/index.js +++ b/src/index.js @@ -23,7 +23,7 @@ class TrustWeb3Provider extends EventEmitter { this.callbacks = new Map(); this.wrapResults = new Map(); this.isTrust = true; - this.isDebug = false; + this.isDebug = !!config.isDebug; this.emitConnect(config.chainId); } @@ -38,6 +38,7 @@ class TrustWeb3Provider extends EventEmitter { this.chainId = config.chainId; this.rpc = new RPCServer(config.rpcUrl); + this.isDebug = !!config.isDebug; } request(payload) { @@ -272,11 +273,17 @@ class TrustWeb3Provider extends EventEmitter { */ postMessage(handler, id, data) { if (this.ready || handler === "requestAccounts") { - window.webkit.messageHandlers[handler].postMessage({ + let object = { + id: id, name: handler, object: data, - id: id, - }); + }; + if (window.trustwallet.postMessage) { + window.trustwallet.postMessage(object); + } else { + // old clients + window.webkit.messageHandlers[handler].postMessage(object); + } } else { // don't forget to verify in the app this.sendError(id, new ProviderRpcError(4100, "provider is not ready")); @@ -335,5 +342,8 @@ class TrustWeb3Provider extends EventEmitter { } } -window.Trust = TrustWeb3Provider; -window.Web3 = Web3; +window.trustwallet = { + Provider: TrustWeb3Provider, + Web3: Web3, + postMessage: null +}; diff --git a/src/tests/test.js b/src/tests/test.js index 206836d4..3f43c307 100644 --- a/src/tests/test.js +++ b/src/tests/test.js @@ -14,7 +14,7 @@ const Web3 = require("web3"); const mainnet = { address: "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", chainId: 1, - rpcUrl: "https://mainnet.infura.io/v3/6e822818ec644335be6f0ed231f48310" + rpcUrl: "https://mainnet.infura.io/v3/6e822818ec644335be6f0ed231f48310", }; const ropsten = { @@ -27,19 +27,19 @@ const bsc = { address: "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", chainId: 56, rpcUrl: "https://bsc-dataseed1.binance.orge", -} +}; describe("TrustWeb3Provider constructor tests", () => { test("test constructor.name", () => { - const provider = new Trust({}); - const web3 = new Web3(provider); + const provider = new trustwallet.Provider({}); + const web3 = new trustwallet.Web3(provider); expect(web3.currentProvider.constructor.name).toBe("TrustWeb3Provider"); }); test("test setAddress", () => { - const provider = new Trust({ + const provider = new trustwallet.Provider({ chainId: 1, - rpcUrl: "" + rpcUrl: "", }); const address = mainnet.address; expect(provider.address).toBe(""); @@ -49,9 +49,8 @@ describe("TrustWeb3Provider constructor tests", () => { expect(provider.ready).toBeTruthy(); }); - test("test setConfig", done => { - - const provider = new Trust(ropsten); + test("test setConfig", (done) => { + const provider = new trustwallet.Provider(ropsten); const web3 = new Web3(provider); expect(web3.currentProvider.chainId).toEqual(3); @@ -69,11 +68,11 @@ describe("TrustWeb3Provider constructor tests", () => { }); }); - test("test eth_chainId", done => { - const provider = new Trust(bsc); + test("test eth_chainId", (done) => { + const provider = new trustwallet.Provider(bsc); const web3 = new Web3(provider); - let request = {jsonrpc: "2.0", method: "eth_chainId", id: 123}; + let request = { jsonrpc: "2.0", method: "eth_chainId", id: 123 }; provider.request(request).then((chainId) => { expect(chainId).toEqual("0x38"); @@ -89,8 +88,8 @@ describe("TrustWeb3Provider constructor tests", () => { }); }); - test("test eth_accounts", done => { - const provider = new Trust(mainnet); + test("test eth_accounts", (done) => { + const provider = new trustwallet.Provider(mainnet); const web3 = new Web3(provider); const addresses = ["0x9d8a62f656a8d1615c1294fd71e9cfb3e4855a4f"]; @@ -99,34 +98,34 @@ describe("TrustWeb3Provider constructor tests", () => { done(); }); - provider.request({method: "eth_accounts"}).then((accounts) => { + provider.request({ method: "eth_accounts" }).then((accounts) => { expect(accounts).toEqual(addresses); done(); }); - web3.currentProvider.sendAsync({method: "eth_accounts"}, (error, data) => { - expect(data.result).toEqual(addresses); - done(); - }); + web3.currentProvider.sendAsync( + { method: "eth_accounts" }, + (error, data) => { + expect(data.result).toEqual(addresses); + done(); + } + ); }); - test("test eth_sign", done => { - const provider = new Trust(mainnet); + test("test eth_sign", (done) => { + const provider = new trustwallet.Provider(mainnet); const web3 = new Web3(provider); const addresses = ["0x9d8a62f656a8d1615c1294fd71e9cfb3e4855a4f"]; - const signed = "0x730ec377cfc7090e08366fad4758aad721dbb51e187efe45426a7e56d1ff053947ab1a7b0bd7b138c48a9f3d3b92bd83f4265abbe9876930faaf7fbb980b219d1c"; - - window.webkit = { - messageHandlers: { - signMessage: { - postMessage: (message) => { - provider.sendResponse(message.id, signed); - } - } - } + const signed = + "0x730ec377cfc7090e08366fad4758aad721dbb51e187efe45426a7e56d1ff053947ab1a7b0bd7b138c48a9f3d3b92bd83f4265abbe9876930faaf7fbb980b219d1c"; + + trustwallet.postMessage = (message) => { + provider.sendResponse(message.id, signed); }; - var hash = ethUtil.keccak256(Buffer.from("An amazing message, for use with MetaMask!", "utf8")); + var hash = ethUtil.keccak256( + Buffer.from("An amazing message, for use with MetaMask!", "utf8") + ); var hex = "0x" + hash.toString("hex"); web3.eth.sign(addresses[0], hex, (err, result) => { expect(result).toEqual(signed); @@ -134,36 +133,33 @@ describe("TrustWeb3Provider constructor tests", () => { }); }); - test("test personal_sign", done => { - const provider = new Trust(bsc); - const signed = "0xf3a9e21a3238b025b7edf5013876548cfb2f2a838aca573de88c91ea9aecf7190cd6330a0172bd5d106841647831f30065f644eddc2f86091e1bb370c9ff833f1c"; - - window.webkit = { - messageHandlers: { - signPersonalMessage: { - postMessage: (message) => { - const buffer = Buffer.from(message.object.data); - if (buffer.length === 0) { - throw new Error("message is not hex!") - } - provider.sendResponse(message.id, signed); - } - } + test("test personal_sign", (done) => { + const provider = new trustwallet.Provider(bsc); + const signed = + "0xf3a9e21a3238b025b7edf5013876548cfb2f2a838aca573de88c91ea9aecf7190cd6330a0172bd5d106841647831f30065f644eddc2f86091e1bb370c9ff833f1c"; + + trustwallet.postMessage = (message) => { + const buffer = Buffer.from(message.object.data); + if (buffer.length === 0) { + throw new Error("message is not hex!"); } + provider.sendResponse(message.id, signed); }; const request = { - "method": "personal_sign", - "params": ["{\"version\":\"0.1.2\",\"timestamp\":\"1602823075\",\"token\":\"0x4b0f1812e5df2a09796481ff14017e6005508003\",\"type\":\"vote\",\"payload\":{\"proposal\":\"QmSV53XuYi28XfdNHDhBVp2ZQwzeewQNBcaDedRi9PC6eY\",\"choice\":1,\"metadata\":{}}}", "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"], - "id": 1602823075454 + method: "personal_sign", + params: [ + '{"version":"0.1.2","timestamp":"1602823075","token":"0x4b0f1812e5df2a09796481ff14017e6005508003","type":"vote","payload":{"proposal":"QmSV53XuYi28XfdNHDhBVp2ZQwzeewQNBcaDedRi9PC6eY","choice":1,"metadata":{}}}', + "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", + ], + id: 1602823075454, }; - expect(Buffer.from(request.params[0], 'hex').length).toEqual(0); + expect(Buffer.from(request.params[0], "hex").length).toEqual(0); - provider.request(request).then(result => { + provider.request(request).then((result) => { expect(result).toEqual(signed); done(); }); }); - }); // end of top describe()