├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ └── activity_main.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── trustwallet │ │ │ │ └── walletconnect │ │ │ │ └── sample │ │ │ │ └── MainActivity.kt │ │ └── AndroidManifest.xml │ └── test │ │ └── java │ │ └── com │ │ └── trustwallet │ │ └── walletconnect │ │ └── sample │ │ └── ExampleUnitTest.kt ├── proguard-rules.pro └── build.gradle ├── walletconnect ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ └── values │ │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── trustwallet │ │ │ └── walletconnect │ │ │ ├── models │ │ │ ├── WCAccount.kt │ │ │ ├── WCSignTransaction.kt │ │ │ ├── WCEncryptionPayload.kt │ │ │ ├── WCSocketMessage.kt │ │ │ ├── session │ │ │ │ ├── WCSessionUpdate.kt │ │ │ │ ├── WCSessionRequest.kt │ │ │ │ ├── WCApproveSessionResponse.kt │ │ │ │ └── WCSession.kt │ │ │ ├── MessageType.kt │ │ │ ├── WCPeerMeta.kt │ │ │ ├── ethereum │ │ │ │ ├── WCEthereumTransaction.kt │ │ │ │ └── WCEthereumSignMessage.kt │ │ │ ├── okexchain │ │ │ │ └── WCOKExChainTransaction.kt │ │ │ ├── binance │ │ │ │ ├── WCBinanceOrder.kt │ │ │ │ ├── WCBinanceTradePair.kt │ │ │ │ ├── WCBinanceCancelOrder.kt │ │ │ │ ├── WCBinanceTransferOrder.kt │ │ │ │ └── WCBinanceTradeOrder.kt │ │ │ └── WCMethod.kt │ │ │ ├── exceptions │ │ │ ├── InvalidMessageException.kt │ │ │ ├── InvalidSessionException.kt │ │ │ ├── InvalidUriException.kt │ │ │ ├── InvalidPayloadException.kt │ │ │ ├── InvalidHmacException.kt │ │ │ ├── InvalidJsonRpcParamsException.kt │ │ │ └── RequiredFieldException.kt │ │ │ ├── jsonrpc │ │ │ ├── JsonRpcRequest.kt │ │ │ ├── JsonRpcResponse.kt │ │ │ └── JsonRpcError.kt │ │ │ ├── extensions │ │ │ ├── ByteArray.kt │ │ │ └── String.kt │ │ │ ├── WCSessionStore.kt │ │ │ ├── WCSessionStoreType.kt │ │ │ ├── WCCipher.kt │ │ │ └── WCClient.kt │ └── test │ │ └── java │ │ └── com │ │ └── trustwallet │ │ └── walletconnect │ │ ├── models │ │ ├── binance │ │ │ ├── WCBinanceTradePairTests.kt │ │ │ └── WCBinanceOrderTests.kt │ │ └── WCSessionTests.kt │ │ ├── WCSessionStoreTests.kt │ │ └── WCCipherTests.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── runConfigurations.xml └── gradle.xml ├── gradle.properties ├── gradlew.bat ├── .gitignore └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /walletconnect/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':walletconnect' 2 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | WalletConnect 3 | 4 | -------------------------------------------------------------------------------- /walletconnect/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | WalletConnect 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /walletconnect/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalletConnect/wallet-connect-kotlin/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCAccount.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models 2 | 3 | data class WCAccount( 4 | val network: Int, 5 | val address: String 6 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCSignTransaction.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models 2 | 3 | data class WCSignTransaction( 4 | val network: Int, 5 | val transaction: String 6 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidMessageException.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.exceptions 2 | 3 | import java.lang.Exception 4 | 5 | class InvalidMessageException : Exception("Invalid message") -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidSessionException.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.exceptions 2 | 3 | import java.lang.Exception 4 | 5 | class InvalidSessionException : Exception("Invalid session") -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidUriException.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.exceptions 2 | 3 | import java.lang.Exception 4 | 5 | class InvalidUriException : Exception("Invalid Wallet Connect URI") -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCEncryptionPayload.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models 2 | 3 | data class WCEncryptionPayload( 4 | val data: String, 5 | val hmac: String, 6 | val iv: String 7 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCSocketMessage.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models 2 | 3 | data class WCSocketMessage( 4 | val topic: String, 5 | val type: MessageType, 6 | val payload: String 7 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidPayloadException.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.exceptions 2 | 3 | import java.lang.Exception 4 | 5 | class InvalidPayloadException : Exception("Invalid WCEncryptionPayload") -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidHmacException.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.exceptions 2 | 3 | import java.lang.Exception 4 | 5 | class InvalidHmacException : Exception("Received and computed HMAC doesn't mach") -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSessionUpdate.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.session 2 | 3 | data class WCSessionUpdate( 4 | val approved: Boolean, 5 | val chainId: Int?, 6 | val accounts: List? 7 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/MessageType.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | enum class MessageType { 6 | @SerializedName("pub") PUB, 7 | @SerializedName("sub") SUB 8 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Nov 11 12:09:22 BRT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidJsonRpcParamsException.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.exceptions 2 | 3 | import java.lang.Exception 4 | 5 | class InvalidJsonRpcParamsException(val requestId: Long) : Exception("Invalid JSON RPC Request") 6 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCPeerMeta.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models 2 | 3 | data class WCPeerMeta ( 4 | val name: String, 5 | val url: String, 6 | val description: String? = null, 7 | val icons: List = listOf("") 8 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/RequiredFieldException.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.exceptions 2 | 3 | import com.google.gson.JsonParseException 4 | 5 | class RequiredFieldException(val field: String = "") : JsonParseException("'$field' is required") -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSessionRequest.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.session 2 | 3 | import com.trustwallet.walletconnect.models.WCPeerMeta 4 | 5 | data class WCSessionRequest( 6 | val peerId: String, 7 | val peerMeta: WCPeerMeta, 8 | val chainId: String? 9 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumTransaction.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.ethereum 2 | 3 | data class WCEthereumTransaction( 4 | val from: String, 5 | val to: String?, 6 | val nonce: String?, 7 | val gasPrice: String?, 8 | val gasLimit: String?, 9 | val value: String?, 10 | val data: String 11 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcRequest.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.jsonrpc 2 | 3 | import com.trustwallet.walletconnect.JSONRPC_VERSION 4 | import com.trustwallet.walletconnect.models.WCMethod 5 | 6 | data class JsonRpcRequest( 7 | val id: Long, 8 | val jsonrpc: String = JSONRPC_VERSION, 9 | val method: WCMethod?, 10 | val params: T 11 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCApproveSessionResponse.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.session 2 | 3 | import com.trustwallet.walletconnect.models.WCPeerMeta 4 | 5 | data class WCApproveSessionResponse( 6 | val approved: Boolean = true, 7 | val chainId: Int, 8 | val accounts: List, 9 | val peerId: String?, 10 | val peerMeta: WCPeerMeta? 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/trustwallet/walletconnect/sample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.sample 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.os.Bundle 5 | 6 | class MainActivity : AppCompatActivity() { 7 | 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | setContentView(R.layout.activity_main) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcResponse.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.jsonrpc 2 | 3 | import com.trustwallet.walletconnect.JSONRPC_VERSION 4 | 5 | data class JsonRpcResponse ( 6 | val jsonrpc: String = JSONRPC_VERSION, 7 | val id: Long, 8 | val result: T 9 | ) 10 | 11 | data class JsonRpcErrorResponse ( 12 | val jsonrpc: String = JSONRPC_VERSION, 13 | val id: Long, 14 | val error: JsonRpcError 15 | ) -------------------------------------------------------------------------------- /app/src/test/java/com/trustwallet/walletconnect/sample/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.sample 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/okexchain/WCOKExChainTransaction.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.okexchain 2 | 3 | data class WCOKExChainTransaction( 4 | val from: String, 5 | val to: String?, 6 | val value: String?, 7 | val gasLimit: String?, 8 | val gasPrice: String?, 9 | val accountNumber: String?, 10 | val sequenceNumber: String?, 11 | val symbol: String?, 12 | val contractAddress: String?, 13 | val data: String? 14 | ) -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/extensions/ByteArray.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.extensions 2 | 3 | private val HEX_CHARS = "0123456789abcdef".toCharArray() 4 | 5 | fun ByteArray.toHex() : String{ 6 | val result = StringBuffer() 7 | 8 | forEach { 9 | val octet = it.toInt() 10 | val firstIndex = (octet and 0xF0).ushr(4) 11 | val secondIndex = octet and 0x0F 12 | result.append(HEX_CHARS[firstIndex]) 13 | result.append(HEX_CHARS[secondIndex]) 14 | } 15 | 16 | return result.toString() 17 | } -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/WCSessionStore.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect 2 | 3 | import com.trustwallet.walletconnect.models.WCPeerMeta 4 | import com.trustwallet.walletconnect.models.session.WCSession 5 | import java.util.* 6 | 7 | data class WCSessionStoreItem( 8 | val session: WCSession, 9 | val peerId: String, 10 | val remotePeerId: String, 11 | val remotePeerMeta: WCPeerMeta, 12 | val isAutoSign: Boolean = false, 13 | val date: Date = Date() 14 | ) 15 | 16 | interface WCSessionStore { 17 | var session: WCSessionStoreItem? 18 | } -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/extensions/String.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.extensions 2 | 3 | private val HEX_CHARS = "0123456789abcdef" 4 | 5 | fun String.hexStringToByteArray() : ByteArray { 6 | val hex = toLowerCase() 7 | val result = ByteArray(length / 2) 8 | 9 | for (i in 0 until hex.length step 2) { 10 | val firstIndex = HEX_CHARS.indexOf(hex[i]) 11 | val secondIndex = HEX_CHARS.indexOf(hex[i + 1]) 12 | 13 | val octet = firstIndex.shl(4).or(secondIndex) 14 | result.set(i.shr(1), octet.toByte()) 15 | } 16 | 17 | return result 18 | } -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceOrder.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.binance 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | open class WCBinanceOrder( 6 | @SerializedName("account_number") 7 | val accountNumber: String, 8 | @SerializedName("chain_id") 9 | val chainId: String, 10 | val data: String?, 11 | val memo: String?, 12 | val sequence: String, 13 | val source: String, 14 | val msgs: List 15 | ) 16 | 17 | data class WCBinanceTxConfirmParam( 18 | val ok: Boolean, 19 | val errorMsg: String? 20 | ) 21 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcError.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.jsonrpc 2 | 3 | data class JsonRpcError ( 4 | val code: Int, 5 | val message: String 6 | ) { 7 | companion object { 8 | fun serverError(message: String) = JsonRpcError(-32000, message) 9 | fun invalidParams(message: String) = JsonRpcError(-32602, message) 10 | fun invalidRequest(message: String) = JsonRpcError(-32600, message) 11 | fun parseError(message: String) = JsonRpcError(-32700, message) 12 | fun methodNotFound(message: String) = JsonRpcError(-32601, message) 13 | } 14 | } -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradePair.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.binance 2 | 3 | data class WCBinanceTradePair(val from: String, val to: String) { 4 | companion object { 5 | fun from(symbol: String): WCBinanceTradePair? { 6 | val pair = symbol.split("_") 7 | 8 | return if (pair.size > 1) { 9 | val firstParts = pair[0].split("-") 10 | val secondParts = pair[1].split("-") 11 | WCBinanceTradePair(firstParts[0], secondParts[0]) 12 | } else { 13 | null 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /walletconnect/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # Kotlin code style for this project: "official" or "obsolete": 15 | kotlin.code.style=official 16 | -------------------------------------------------------------------------------- /walletconnect/src/test/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradePairTests.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.binance 2 | 3 | import org.junit.Assert.* 4 | import org.junit.Test 5 | 6 | class WCBinanceTradePairTests { 7 | @Test 8 | fun test_parse() { 9 | val symbol = "BNB_ETH.B-261" 10 | val pair = WCBinanceTradePair.from(symbol) 11 | 12 | assertEquals(pair?.from, "BNB") 13 | assertEquals(pair?.to, "ETH.B") 14 | 15 | val symbol2 = "000-0E1_BNB" 16 | val pair2 = WCBinanceTradePair.from(symbol2) 17 | 18 | assertEquals(pair2?.from, "000") 19 | assertEquals(pair2?.to, "BNB") 20 | 21 | val symbol3 = "CRYPRICE-150_BTC.B-918" 22 | val pair3 = WCBinanceTradePair.from(symbol3) 23 | 24 | assertEquals(pair3?.from, "CRYPRICE") 25 | assertEquals(pair3?.to, "BTC.B") 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumSignMessage.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.ethereum 2 | 3 | data class WCEthereumSignMessage ( 4 | val raw: List, 5 | val type: WCSignType 6 | ) { 7 | enum class WCSignType { 8 | MESSAGE, PERSONAL_MESSAGE, TYPED_MESSAGE 9 | } 10 | 11 | /** 12 | * Raw parameters will always be the message and the addess. Depending on the WCSignType, 13 | * those parameters can be swapped as description below: 14 | * 15 | * - MESSAGE: `[address, data ]` 16 | * - TYPED_MESSAGE: `[address, data]` 17 | * - PERSONAL_MESSAGE: `[data, address]` 18 | * 19 | * reference: https://docs.walletconnect.org/json-rpc/ethereum#eth_signtypeddata 20 | */ 21 | val data get() = when (type) { 22 | WCSignType.MESSAGE -> raw[1] 23 | WCSignType.TYPED_MESSAGE -> raw[1] 24 | WCSignType.PERSONAL_MESSAGE -> raw[0] 25 | } 26 | } -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSession.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.session 2 | 3 | import android.net.Uri 4 | 5 | data class WCSession ( 6 | val topic: String, 7 | val version: String, 8 | val bridge: String, 9 | val key: String 10 | ) { 11 | fun toUri(): String = "wc:${topic}@${version}?bridge=${bridge}&key=${key}" 12 | 13 | companion object { 14 | fun from(from: String): WCSession? { 15 | if (!from.startsWith("wc:")) { 16 | return null 17 | } 18 | 19 | val uriString = from.replace("wc:", "wc://") 20 | val uri = Uri.parse(uriString) 21 | val bridge = uri.getQueryParameter("bridge") 22 | val key = uri.getQueryParameter("key") 23 | val topic = uri.userInfo 24 | val version = uri.host 25 | 26 | if (bridge == null || key == null || topic == null || version == null) { 27 | return null 28 | } 29 | 30 | return WCSession(topic, version, bridge, key) 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /walletconnect/src/test/java/com/trustwallet/walletconnect/models/WCSessionTests.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models 2 | 3 | import android.os.Build 4 | import com.trustwallet.walletconnect.models.session.WCSession 5 | import org.junit.Test 6 | 7 | import org.junit.Assert.* 8 | import org.junit.runner.RunWith 9 | import org.robolectric.RobolectricTestRunner 10 | import org.robolectric.annotation.Config 11 | 12 | @RunWith(RobolectricTestRunner::class) 13 | @Config(sdk = [Build.VERSION_CODES.O_MR1]) 14 | class WCSessionTests { 15 | 16 | @Test 17 | fun test_from() { 18 | val uri = "wc:217374f6-8735-472d-a743-23bd7d26d106@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=d565a3e6cc792fa789bbea26b3f257fb436cfba2de48d2490b3e0248168d4b6b" 19 | val session = WCSession.from(uri) 20 | 21 | assertEquals("217374f6-8735-472d-a743-23bd7d26d106", session?.topic) 22 | assertEquals("1", session?.version) 23 | assertEquals("https://bridge.walletconnect.org", session?.bridge) 24 | assertEquals("d565a3e6cc792fa789bbea26b3f257fb436cfba2de48d2490b3e0248168d4b6b", session?.key) 25 | } 26 | } -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCMethod.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | enum class WCMethod { 6 | @SerializedName("wc_sessionRequest") 7 | SESSION_REQUEST, 8 | 9 | @SerializedName("wc_sessionUpdate") 10 | SESSION_UPDATE, 11 | 12 | @SerializedName("eth_sign") 13 | ETH_SIGN, 14 | 15 | @SerializedName("personal_sign") 16 | ETH_PERSONAL_SIGN, 17 | 18 | @SerializedName("eth_signTypedData") 19 | ETH_SIGN_TYPE_DATA, 20 | 21 | @SerializedName("eth_signTransaction") 22 | ETH_SIGN_TRANSACTION, 23 | 24 | @SerializedName("eth_sendTransaction") 25 | ETH_SEND_TRANSACTION, 26 | 27 | @SerializedName("bnb_sign") 28 | BNB_SIGN, 29 | 30 | @SerializedName("bnb_tx_confirmation") 31 | BNB_TRANSACTION_CONFIRM, 32 | 33 | @SerializedName("get_accounts") 34 | GET_ACCOUNTS, 35 | 36 | @SerializedName("trust_signTransaction") 37 | SIGN_TRANSACTION, 38 | 39 | @SerializedName("okt_signTransaction") 40 | OKT_SIGN_TRANSACTION, 41 | 42 | @SerializedName("okt_sendTransaction") 43 | OKT_SEND_TRANSACTION; 44 | 45 | } -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/WCSessionStoreType.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect 2 | 3 | import android.content.SharedPreferences 4 | import com.github.salomonbrys.kotson.* 5 | import com.google.gson.GsonBuilder 6 | 7 | class WCSessionStoreType( 8 | private val sharedPreferences: SharedPreferences, 9 | builder: GsonBuilder = GsonBuilder() 10 | ): WCSessionStore { 11 | private val gson = builder 12 | .serializeNulls() 13 | .create() 14 | 15 | private fun store(item: WCSessionStoreItem?) { 16 | if (item != null) { 17 | sharedPreferences.edit().putString(SESSION_KEY, gson.toJson(item)).apply() 18 | } else { 19 | sharedPreferences.edit().remove(SESSION_KEY).apply() 20 | } 21 | } 22 | 23 | private fun load(): WCSessionStoreItem? { 24 | val json = sharedPreferences.getString(SESSION_KEY, null) ?: return null 25 | return gson.fromJson(json) 26 | } 27 | 28 | override var session: WCSessionStoreItem? 29 | set(item) = store(item) 30 | get() = load() 31 | 32 | companion object { 33 | private const val SESSION_KEY = "org.walletconnect.session" 34 | } 35 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 28 9 | defaultConfig { 10 | applicationId "com.trustwallet.walletconnect.sample" 11 | minSdkVersion 21 12 | targetSdkVersion 28 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 28 | implementation 'com.android.support:appcompat-v7:28.0.0' 29 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 30 | testImplementation 'junit:junit:4.12' 31 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 33 | } 34 | -------------------------------------------------------------------------------- /walletconnect/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | group='com.github.TrustWallet' 6 | 7 | android { 8 | compileSdkVersion 28 9 | 10 | 11 | defaultConfig { 12 | minSdkVersion 21 13 | targetSdkVersion 28 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | 28 | testOptions { 29 | unitTests { 30 | includeAndroidResources = true 31 | } 32 | } 33 | compileOptions { 34 | sourceCompatibility = "1.8" 35 | targetCompatibility = 1.8 36 | } 37 | buildToolsVersion = '28.0.3' 38 | } 39 | 40 | dependencies { 41 | implementation fileTree(dir: 'libs', include: ['*.jar']) 42 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 43 | implementation 'com.squareup.okhttp3:okhttp:4.0.1' 44 | implementation 'com.google.code.gson:gson:2.8.5' 45 | implementation 'com.github.salomonbrys.kotson:kotson:2.5.0' 46 | implementation 'com.android.support:appcompat-v7:28.0.0' 47 | testImplementation 'junit:junit:4.12' 48 | testImplementation 'org.robolectric:robolectric:4.3' 49 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 50 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 51 | } 52 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceCancelOrder.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.binance 2 | 3 | import com.github.salomonbrys.kotson.get 4 | import com.github.salomonbrys.kotson.jsonDeserializer 5 | import com.github.salomonbrys.kotson.jsonSerializer 6 | import com.github.salomonbrys.kotson.string 7 | import com.google.gson.JsonObject 8 | 9 | class WCBinanceCancelOrder( 10 | account_number: String, 11 | chain_id: String, 12 | data: String?, 13 | memo: String?, 14 | sequence: String, 15 | source: String, 16 | msgs: List 17 | ): WCBinanceOrder(account_number, chain_id, data, memo, sequence, source, msgs) { 18 | 19 | enum class MessageKey(val key: String) { 20 | REFID("refid"), 21 | SENDER("sender"), 22 | SYMBOL("symbol") 23 | } 24 | 25 | data class Message( 26 | val refid: String, 27 | val sender: String, 28 | val symbol: String 29 | ) 30 | } 31 | 32 | val cancelOrderDeserializer = jsonDeserializer { 33 | WCBinanceCancelOrder.Message( 34 | refid = it.json[WCBinanceCancelOrder.MessageKey.REFID.key].string, 35 | sender = it.json[WCBinanceCancelOrder.MessageKey.SENDER.key].string, 36 | symbol = it.json[WCBinanceCancelOrder.MessageKey.SYMBOL.key].string 37 | ) 38 | } 39 | 40 | val cancelOrderSerializer = jsonSerializer { 41 | val jsonObject = JsonObject() 42 | jsonObject.addProperty("refid", it.src.refid) 43 | jsonObject.addProperty("sender", it.src.sender) 44 | jsonObject.addProperty("symbol", it.src.symbol) 45 | jsonObject 46 | } -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTransferOrder.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.binance 2 | 3 | import com.github.salomonbrys.kotson.* 4 | import com.google.gson.JsonObject 5 | 6 | class WCBinanceTransferOrder( 7 | account_number: String, 8 | chain_id: String, 9 | data: String?, 10 | memo: String?, 11 | sequence: String, 12 | source: String, 13 | msgs: List 14 | ): WCBinanceOrder(account_number, chain_id, data, memo, sequence, source, msgs) { 15 | 16 | enum class MessageKey(val key: String) { 17 | INPUTS("inputs"), 18 | OUTPUTS("outputs") 19 | } 20 | 21 | data class Message( 22 | val inputs: List, 23 | val outputs: List 24 | ) { 25 | 26 | data class Item( 27 | val address: String, 28 | val coins: List 29 | ) { 30 | 31 | data class Coin( 32 | val amount: Long, 33 | val denom: String 34 | ) 35 | } 36 | } 37 | } 38 | 39 | val transferOrderDeserializer = jsonDeserializer { 40 | WCBinanceTransferOrder.Message( 41 | inputs = it.context.deserialize(it.json[WCBinanceTransferOrder.MessageKey.INPUTS.key].array), 42 | outputs = it.context.deserialize(it.json[WCBinanceTransferOrder.MessageKey.OUTPUTS.key].array) 43 | ) 44 | } 45 | 46 | val transferOrderSerializer = jsonSerializer { 47 | val jsonObject = JsonObject() 48 | jsonObject.addProperty(WCBinanceTransferOrder.MessageKey.INPUTS.key, it.context.serialize(it.src.inputs)) 49 | jsonObject.addProperty(WCBinanceTransferOrder.MessageKey.OUTPUTS.key, it.context.serialize(it.src.outputs)) 50 | jsonObject 51 | } -------------------------------------------------------------------------------- /walletconnect/src/test/java/com/trustwallet/walletconnect/WCSessionStoreTests.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect 2 | 3 | import android.content.Context 4 | import android.os.Build 5 | import com.trustwallet.walletconnect.models.WCPeerMeta 6 | import com.trustwallet.walletconnect.models.session.WCSession 7 | import org.junit.Assert 8 | import org.junit.Before 9 | import org.junit.Test 10 | import org.junit.runner.RunWith 11 | import org.robolectric.RobolectricTestRunner 12 | import org.robolectric.RuntimeEnvironment 13 | import org.robolectric.annotation.Config 14 | 15 | @RunWith(RobolectricTestRunner::class) 16 | @Config(sdk = [Build.VERSION_CODES.M]) 17 | class WCSessionStoreTests { 18 | private val context = RuntimeEnvironment.systemContext 19 | private val sharedPreferences = context.getSharedPreferences("tests", Context.MODE_PRIVATE) 20 | private val storage = WCSessionStoreType(sharedPreferences) 21 | 22 | companion object { 23 | const val SESSION_KEY = "org.walletconnect.session" 24 | } 25 | 26 | @Before 27 | fun before() { 28 | sharedPreferences.edit().clear().commit() 29 | } 30 | 31 | @Test 32 | fun test_store() { 33 | val topic = "topic_1" 34 | val session = WCSession.from("wc:$topic@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=some_key")!! 35 | val item = WCSessionStoreItem(session, "peerId", "remotePeerId", WCPeerMeta(name = "Some DApp", url = "https://dapp.com")) 36 | 37 | storage.session = item 38 | Assert.assertNotNull(sharedPreferences.getString(SESSION_KEY, null)) 39 | } 40 | 41 | @Test 42 | fun test_remove() { 43 | val topic = "topic_1" 44 | val session = WCSession.from("wc:$topic@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=some_key")!! 45 | val item = WCSessionStoreItem(session, "peerId","remotePeerId", WCPeerMeta(name = "Some DApp", url = "https://dapp.com")) 46 | 47 | storage.session = item 48 | storage.session = null 49 | Assert.assertFalse(sharedPreferences.contains(SESSION_KEY)) 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /walletconnect/src/test/java/com/trustwallet/walletconnect/WCCipherTests.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect 2 | 3 | import android.os.Build 4 | import com.trustwallet.walletconnect.extensions.hexStringToByteArray 5 | import com.trustwallet.walletconnect.models.WCEncryptionPayload 6 | import org.junit.Assert 7 | import org.junit.Test 8 | import org.junit.runner.RunWith 9 | import org.robolectric.RobolectricTestRunner 10 | import org.robolectric.annotation.Config 11 | 12 | @RunWith(RobolectricTestRunner::class) 13 | @Config(sdk = [Build.VERSION_CODES.O_MR1]) 14 | class WCCipherTests { 15 | @Test 16 | fun test_decrypt() { 17 | val data = "1b3db3674de082d65455eba0ae61cfe7e681c8ef1132e60c8dbd8e52daf18f4fea42cc76366c83351dab6dca52682ff81f828753f89a21e1cc46587ca51ccd353914ffdd3b0394acfee392be6c22b3db9237d3f717a3777e3577dd70408c089a4c9c85130a68c43b0a8aadb00f1b8a8558798104e67aa4ff027b35d4b989e7fd3988d5dcdd563105767670be735b21c4" 18 | val hmac = "a33f868e793ca4fcca964bcb64430f65e2f1ca7a779febeaf94c5373d6df48b3" 19 | val iv = "89ef1d6728bac2f1dcde2ef9330d2bb8" 20 | val key = "5caa3a74154cee16bd1b570a1330be46e086474ac2f4720530662ef1a469662c".hexStringToByteArray() 21 | val payload = WCEncryptionPayload( 22 | data = data, 23 | iv = iv, 24 | hmac = hmac 25 | ) 26 | 27 | val decrypted = String(WCCipher.decrypt(payload, key), Charsets.UTF_8) 28 | val expected = "{\"id\":1554098597199736,\"jsonrpc\":\"2.0\",\"method\":\"wc_sessionUpdate\",\"params\":[{\"approved\":false,\"chainId\":null,\"accounts\":null}]}" 29 | Assert.assertEquals(expected, decrypted) 30 | } 31 | 32 | @Test 33 | fun test_encrypt() { 34 | val expected = "{\"id\":1554098597199736,\"jsonrpc\":\"2.0\",\"method\":\"wc_sessionUpdate\",\"params\":[{\"approved\":false,\"chainId\":null,\"accounts\":null}]}".hexStringToByteArray() 35 | val key = "5caa3a74154cee16bd1b570a1330be46e086474ac2f4720530662ef1a469662c".hexStringToByteArray() 36 | val payload = WCCipher.encrypt(data = expected, key = key) 37 | val decrypted = WCCipher.decrypt(payload, key) 38 | Assert.assertArrayEquals(expected, decrypted) 39 | } 40 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/WCCipher.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect 2 | 3 | import com.trustwallet.walletconnect.exceptions.InvalidHmacException 4 | import com.trustwallet.walletconnect.extensions.hexStringToByteArray 5 | import com.trustwallet.walletconnect.extensions.toHex 6 | import com.trustwallet.walletconnect.models.WCEncryptionPayload 7 | import java.security.SecureRandom 8 | import javax.crypto.Cipher 9 | import javax.crypto.Mac 10 | import javax.crypto.spec.IvParameterSpec 11 | import javax.crypto.spec.SecretKeySpec 12 | 13 | private val CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding" 14 | private val MAC_ALGORITHM = "HmacSHA256" 15 | 16 | object WCCipher { 17 | fun encrypt(data: ByteArray, key: ByteArray): WCEncryptionPayload { 18 | val iv = randomBytes(16) 19 | val keySpec = SecretKeySpec(key, "AES") 20 | val ivSpec = IvParameterSpec(iv) 21 | val cipher = Cipher.getInstance(CIPHER_ALGORITHM) 22 | cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec) 23 | 24 | val encryptedData = cipher.doFinal(data) 25 | val hmac = computeHmac( 26 | data = encryptedData, 27 | iv = iv, 28 | key = key 29 | ) 30 | 31 | return WCEncryptionPayload( 32 | data = encryptedData.toHex(), 33 | iv = iv.toHex(), 34 | hmac = hmac 35 | ) 36 | } 37 | 38 | fun decrypt(payload: WCEncryptionPayload, key: ByteArray): ByteArray { 39 | val data = payload.data.hexStringToByteArray() 40 | val iv = payload.iv.hexStringToByteArray() 41 | val computedHmac = computeHmac( 42 | data = data, 43 | iv = iv, 44 | key = key 45 | ) 46 | 47 | if (computedHmac != payload.hmac.toLowerCase()) { 48 | throw InvalidHmacException() 49 | } 50 | 51 | val keySpec = SecretKeySpec(key, "AES") 52 | val ivSpec = IvParameterSpec(iv) 53 | val cipher = Cipher.getInstance(CIPHER_ALGORITHM) 54 | cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec) 55 | 56 | return cipher.doFinal(data) 57 | } 58 | 59 | private fun computeHmac(data: ByteArray, iv: ByteArray, key: ByteArray): String { 60 | val mac = Mac.getInstance(MAC_ALGORITHM) 61 | val payload = data + iv 62 | mac.init(SecretKeySpec(key, MAC_ALGORITHM)) 63 | return mac.doFinal(payload).toHex() 64 | } 65 | 66 | private fun randomBytes(size: Int): ByteArray { 67 | val secureRandom = SecureRandom() 68 | val bytes = ByteArray(size) 69 | secureRandom.nextBytes(bytes) 70 | 71 | return bytes 72 | } 73 | } -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradeOrder.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.binance 2 | 3 | import com.github.salomonbrys.kotson.* 4 | import com.google.gson.JsonObject 5 | 6 | class WCBinanceTradeOrder( 7 | account_number: String, 8 | chain_id: String, 9 | data: String?, 10 | memo: String?, 11 | sequence: String, 12 | source: String, 13 | msgs: List 14 | ) : WCBinanceOrder (account_number, chain_id, data, memo, sequence, source, msgs) { 15 | 16 | enum class MessageKey(val key: String) { 17 | ID("id"), 18 | ORDER_TYPE("ordertype"), 19 | PRICE("price"), 20 | QUANTITY("quantity"), 21 | SENDER("sender"), 22 | SIDE("side"), 23 | SYMBOL("symbol"), 24 | TIME_INFORCE("timeinforce") 25 | } 26 | 27 | data class Message( 28 | val id: String, 29 | val orderType: Int, 30 | val price: Long, 31 | val quantity: Long, 32 | val sender: String, 33 | val side: Int, 34 | val symbol: String, 35 | val timeInforce: Int 36 | ) 37 | } 38 | 39 | val tradeOrderDeserializer = jsonDeserializer { 40 | WCBinanceTradeOrder.Message( 41 | id = it.json[WCBinanceTradeOrder.MessageKey.ID.key].string, 42 | orderType = it.json[WCBinanceTradeOrder.MessageKey.ORDER_TYPE.key].int, 43 | price = it.json[WCBinanceTradeOrder.MessageKey.PRICE.key].long, 44 | quantity = it.json[WCBinanceTradeOrder.MessageKey.QUANTITY.key].long, 45 | sender = it.json[WCBinanceTradeOrder.MessageKey.SENDER.key].string, 46 | side = it.json[WCBinanceTradeOrder.MessageKey.SIDE.key].int, 47 | symbol = it.json[WCBinanceTradeOrder.MessageKey.SYMBOL.key].string, 48 | timeInforce = it.json[WCBinanceTradeOrder.MessageKey.TIME_INFORCE.key].int 49 | ) 50 | } 51 | 52 | val tradeOrderSerializer = jsonSerializer { 53 | val jsonObject = JsonObject() 54 | jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.ID.key, it.src.id) 55 | jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.ORDER_TYPE.key, it.src.orderType) 56 | jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.PRICE.key, it.src.price) 57 | jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.QUANTITY.key, it.src.quantity) 58 | jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.SENDER.key, it.src.sender) 59 | jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.SIDE.key, it.src.side) 60 | jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.SYMBOL.key, it.src.symbol) 61 | jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.TIME_INFORCE.key, it.src.timeInforce) 62 | 63 | jsonObject 64 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/androidstudio 3 | # Edit at https://www.gitignore.io/?templates=androidstudio 4 | 5 | ### AndroidStudio ### 6 | # Covers files to be ignored for android development using Android Studio. 7 | 8 | # Built application files 9 | *.apk 10 | *.ap_ 11 | 12 | # Files for the ART/Dalvik VM 13 | *.dex 14 | 15 | # Java class files 16 | *.class 17 | 18 | # Generated files 19 | bin/ 20 | gen/ 21 | out/ 22 | 23 | # Gradle files 24 | .gradle 25 | .gradle/ 26 | build/ 27 | 28 | # Signing files 29 | .signing/ 30 | 31 | # Local configuration file (sdk path, etc) 32 | local.properties 33 | 34 | # Proguard folder generated by Eclipse 35 | proguard/ 36 | 37 | # Log Files 38 | *.log 39 | 40 | # Android Studio 41 | /*/build/ 42 | /*/local.properties 43 | /*/out 44 | /*/*/build 45 | /*/*/production 46 | captures/ 47 | .navigation/ 48 | *.ipr 49 | *~ 50 | *.swp 51 | 52 | # Android Patch 53 | gen-external-apklibs 54 | 55 | # External native build folder generated in Android Studio 2.2 and later 56 | .externalNativeBuild 57 | 58 | # NDK 59 | obj/ 60 | 61 | # IntelliJ IDEA 62 | *.iml 63 | *.iws 64 | /out/ 65 | 66 | # User-specific configurations 67 | .idea/caches/ 68 | .idea/libraries/ 69 | .idea/shelf/ 70 | .idea/workspace.xml 71 | .idea/tasks.xml 72 | .idea/.name 73 | .idea/compiler.xml 74 | .idea/copyright/profiles_settings.xml 75 | .idea/encodings.xml 76 | .idea/misc.xml 77 | .idea/modules.xml 78 | .idea/scopes/scope_settings.xml 79 | .idea/dictionaries 80 | .idea/vcs.xml 81 | .idea/jsLibraryMappings.xml 82 | .idea/datasources.xml 83 | .idea/dataSources.ids 84 | .idea/sqlDataSources.xml 85 | .idea/dynamic.xml 86 | .idea/uiDesigner.xml 87 | .idea/assetWizardSettings.xml 88 | 89 | # OS-specific files 90 | .DS_Store 91 | .DS_Store? 92 | ._* 93 | .Spotlight-V100 94 | .Trashes 95 | ehthumbs.db 96 | Thumbs.db 97 | 98 | # Legacy Eclipse project files 99 | .classpath 100 | .project 101 | .cproject 102 | .settings/ 103 | 104 | # Mobile Tools for Java (J2ME) 105 | .mtj.tmp/ 106 | 107 | # Package Files # 108 | *.war 109 | *.ear 110 | 111 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 112 | hs_err_pid* 113 | 114 | ## Plugin-specific files: 115 | 116 | # mpeltonen/sbt-idea plugin 117 | .idea_modules/ 118 | 119 | # JIRA plugin 120 | atlassian-ide-plugin.xml 121 | 122 | # Mongo Explorer plugin 123 | .idea/mongoSettings.xml 124 | 125 | # Crashlytics plugin (for Android Studio and IntelliJ) 126 | com_crashlytics_export_strings.xml 127 | crashlytics.properties 128 | crashlytics-build.properties 129 | fabric.properties 130 | 131 | ### AndroidStudio Patch ### 132 | 133 | !/gradle/wrapper/gradle-wrapper.jar 134 | 135 | # End of https://www.gitignore.io/api/androidstudio 136 | tests.xml -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | xmlns:android 17 | 18 | ^$ 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 | xmlns:.* 28 | 29 | ^$ 30 | 31 | 32 | BY_NAME 33 | 34 |
35 |
36 | 37 | 38 | 39 | .*:id 40 | 41 | http://schemas.android.com/apk/res/android 42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | .*:name 51 | 52 | http://schemas.android.com/apk/res/android 53 | 54 | 55 | 56 |
57 |
58 | 59 | 60 | 61 | name 62 | 63 | ^$ 64 | 65 | 66 | 67 |
68 |
69 | 70 | 71 | 72 | style 73 | 74 | ^$ 75 | 76 | 77 | 78 |
79 |
80 | 81 | 82 | 83 | .* 84 | 85 | ^$ 86 | 87 | 88 | BY_NAME 89 | 90 |
91 |
92 | 93 | 94 | 95 | .* 96 | 97 | http://schemas.android.com/apk/res/android 98 | 99 | 100 | ANDROID_ATTRIBUTE_ORDER 101 | 102 |
103 |
104 | 105 | 106 | 107 | .* 108 | 109 | .* 110 | 111 | 112 | BY_NAME 113 | 114 |
115 |
116 |
117 |
118 | 119 | 121 |
122 |
-------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /walletconnect/src/test/java/com/trustwallet/walletconnect/models/binance/WCBinanceOrderTests.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect.models.binance 2 | 3 | import com.github.salomonbrys.kotson.fromJson 4 | import com.github.salomonbrys.kotson.registerTypeAdapter 5 | import com.google.gson.GsonBuilder 6 | import com.google.gson.JsonArray 7 | import com.trustwallet.walletconnect.jsonrpc.JsonRpcRequest 8 | import org.junit.Assert.assertEquals 9 | import org.junit.Assert.assertNotNull 10 | import org.junit.Test 11 | import java.util.* 12 | 13 | class WCBinanceOrderTests { 14 | val gson = GsonBuilder() 15 | .registerTypeAdapter(cancelOrderDeserializer) 16 | .registerTypeAdapter(cancelOrderSerializer) 17 | .registerTypeAdapter(tradeOrderDeserializer) 18 | .registerTypeAdapter(tradeOrderSerializer) 19 | .registerTypeAdapter(transferOrderDeserializer) 20 | .registerTypeAdapter(transferOrderSerializer) 21 | .create() 22 | 23 | @Test 24 | fun test_parseCancelOrder() { 25 | val json = """ 26 | { 27 | "id": 1, 28 | "jsonrpc": "2.0", 29 | "method": "bnb_sign", 30 | "params": [ 31 | { 32 | "account_number": "29", 33 | "chain_id": "Binance-Chain-Tigris", 34 | "data": null, 35 | "memo": "", 36 | "msgs": [ 37 | { 38 | "refid": "33BBF307B98146F13D20693CF946C2D77A4CAF28-300", 39 | "sender": "bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl", 40 | "symbol": "PVT-554_BNB" 41 | } 42 | ], 43 | "sequence": "300", 44 | "source": "1" 45 | } 46 | ] 47 | }""" 48 | 49 | 50 | val request = gson.fromJson>(json) 51 | val cancelOrder = gson.fromJson>(request.params).first() 52 | assertNotNull(cancelOrder) 53 | val cancelOrderJson = gson.toJson(cancelOrder) 54 | assertEquals(cancelOrderJson, """{"account_number":"29","chain_id":"Binance-Chain-Tigris","memo":"","sequence":"300","source":"1","msgs":[{"refid":"33BBF307B98146F13D20693CF946C2D77A4CAF28-300","sender":"bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl","symbol":"PVT-554_BNB"}]}""") 55 | } 56 | 57 | @Test 58 | fun test_parseTradeOrder() { 59 | val json = """ 60 | { 61 | "id": 1, 62 | "jsonrpc": "2.0", 63 | "method": "bnb_sign", 64 | "params": [ 65 | { 66 | "account_number": "29", 67 | "chain_id": "Binance-Chain-Tigris", 68 | "data": null, 69 | "memo": "", 70 | "msgs": [ 71 | { 72 | "id": "33BBF307B98146F13D20693CF946C2D77A4CAF28-300", 73 | "ordertype": 2, 74 | "price": 7800, 75 | "quantity": 10000000000, 76 | "sender": "bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl", 77 | "side": 1, 78 | "symbol": "PVT-554_BNB", 79 | "timeinforce": 1 80 | } 81 | ], 82 | "sequence": "299", 83 | "source": "1" 84 | } 85 | ] 86 | }""" 87 | 88 | 89 | val request = gson.fromJson>(json) 90 | val tradeOrder = gson.fromJson>(request.params).first() 91 | assertNotNull(tradeOrder) 92 | val cancelOrderJson = gson.toJson(tradeOrder) 93 | assertEquals(cancelOrderJson, """{"account_number":"29","chain_id":"Binance-Chain-Tigris","memo":"","sequence":"299","source":"1","msgs":[{"id":"33BBF307B98146F13D20693CF946C2D77A4CAF28-300","ordertype":2,"price":7800,"quantity":10000000000,"sender":"bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl","side":1,"symbol":"PVT-554_BNB","timeinforce":1}]}""") 94 | } 95 | 96 | @Test 97 | fun test_parseTransferOrder() { 98 | val json = """ 99 | { 100 | "id": 1, 101 | "jsonrpc": "2.0", 102 | "method": "bnb_sign", 103 | "params": [ 104 | { 105 | "account_number": "29", 106 | "chain_id": "Binance-Chain-Tigris", 107 | "data": null, 108 | "memo": "Testing", 109 | "msgs": [ 110 | { 111 | "inputs": [ 112 | { 113 | "address": "bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl", 114 | "coins": [ 115 | { 116 | "amount": 1000000, 117 | "denom": "BNB" 118 | } 119 | ] 120 | } 121 | ], 122 | "outputs": [ 123 | { 124 | "address": "bnb14u7newkxwdhcuhddvtg2n8n96m9tqxejsjuuhn", 125 | "coins": [ 126 | { 127 | "amount": 1000000, 128 | "denom": "BNB" 129 | } 130 | ] 131 | } 132 | ] 133 | } 134 | ], 135 | "sequence": "301", 136 | "source": "1" 137 | } 138 | ] 139 | } 140 | """ 141 | 142 | val request = gson.fromJson>(json) 143 | val order = gson.fromJson>(request.params).first() 144 | assertNotNull(order) 145 | assertEquals(gson.toJson(order), """{"account_number":"29","chain_id":"Binance-Chain-Tigris","memo":"Testing","sequence":"301","source":"1","msgs":[{"inputs":[{"address":"bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl","coins":[{"amount":1000000,"denom":"BNB"}]}],"outputs":[{"address":"bnb14u7newkxwdhcuhddvtg2n8n96m9tqxejsjuuhn","coins":[{"amount":1000000,"denom":"BNB"}]}]}]}""") 146 | } 147 | 148 | @Test(expected = NoSuchElementException::class) 149 | fun test_parseInvalidTradeOrder() { 150 | val json = """ 151 | { 152 | "id": 1, 153 | "jsonrpc": "2.0", 154 | "method": "bnb_sign", 155 | "params": [ 156 | { 157 | "account_number": "29", 158 | "chain_id": "Binance-Chain-Tigris", 159 | "data": null, 160 | "memo": "", 161 | "msgs": [ 162 | { 163 | "refid": "33BBF307B98146F13D20693CF946C2D77A4CAF28-300", 164 | "sender": "bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl", 165 | "symbol": "PVT-554_BNB" 166 | } 167 | ], 168 | "sequence": "300", 169 | "source": "1" 170 | } 171 | ] 172 | }""" 173 | 174 | 175 | val request = gson.fromJson>(json) 176 | gson.fromJson>(request.params).first() 177 | } 178 | } -------------------------------------------------------------------------------- /walletconnect/src/main/java/com/trustwallet/walletconnect/WCClient.kt: -------------------------------------------------------------------------------- 1 | package com.trustwallet.walletconnect 2 | 3 | import android.util.Log 4 | import com.github.salomonbrys.kotson.fromJson 5 | import com.github.salomonbrys.kotson.registerTypeAdapter 6 | import com.github.salomonbrys.kotson.typeToken 7 | import com.google.gson.GsonBuilder 8 | import com.google.gson.JsonArray 9 | import com.trustwallet.walletconnect.exceptions.InvalidJsonRpcParamsException 10 | import com.trustwallet.walletconnect.extensions.hexStringToByteArray 11 | import com.trustwallet.walletconnect.jsonrpc.JsonRpcError 12 | import com.trustwallet.walletconnect.jsonrpc.JsonRpcErrorResponse 13 | import com.trustwallet.walletconnect.jsonrpc.JsonRpcRequest 14 | import com.trustwallet.walletconnect.jsonrpc.JsonRpcResponse 15 | import com.trustwallet.walletconnect.models.* 16 | import com.trustwallet.walletconnect.models.binance.* 17 | import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage 18 | import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction 19 | import com.trustwallet.walletconnect.models.okexchain.WCOKExChainTransaction 20 | import com.trustwallet.walletconnect.models.session.WCApproveSessionResponse 21 | import com.trustwallet.walletconnect.models.session.WCSession 22 | import com.trustwallet.walletconnect.models.session.WCSessionRequest 23 | import com.trustwallet.walletconnect.models.session.WCSessionUpdate 24 | import okhttp3.* 25 | import okio.ByteString 26 | import java.util.* 27 | 28 | const val JSONRPC_VERSION = "2.0" 29 | const val WS_CLOSE_NORMAL = 1000 30 | 31 | open class WCClient ( 32 | builder: GsonBuilder = GsonBuilder(), 33 | private val httpClient: OkHttpClient 34 | ): WebSocketListener() { 35 | private val TAG = "WCClient" 36 | 37 | private val gson = builder 38 | .serializeNulls() 39 | .registerTypeAdapter(cancelOrderSerializer) 40 | .registerTypeAdapter(cancelOrderDeserializer) 41 | .registerTypeAdapter(tradeOrderSerializer) 42 | .registerTypeAdapter(tradeOrderDeserializer) 43 | .registerTypeAdapter(transferOrderSerializer) 44 | .registerTypeAdapter(transferOrderDeserializer) 45 | .create() 46 | 47 | private var socket: WebSocket? = null 48 | 49 | private val listeners: MutableSet = mutableSetOf() 50 | 51 | var session: WCSession? = null 52 | private set 53 | 54 | var peerMeta: WCPeerMeta? = null 55 | private set 56 | 57 | var peerId: String? = null 58 | private set 59 | 60 | var remotePeerId: String? = null 61 | private set 62 | 63 | var isConnected: Boolean = false 64 | private set 65 | 66 | private var handshakeId: Long = -1 67 | 68 | var onFailure: (Throwable) -> Unit = { _ -> Unit} 69 | var onDisconnect: (code: Int, reason: String) -> Unit = { _, _ -> Unit } 70 | var onSessionRequest: (id: Long, peer: WCPeerMeta) -> Unit = { _, _ -> Unit } 71 | var onEthSign: (id: Long, message: WCEthereumSignMessage) -> Unit = { _, _ -> Unit } 72 | var onEthSignTransaction: (id: Long, transaction: WCEthereumTransaction) -> Unit = { _, _ -> Unit } 73 | var onEthSendTransaction: (id: Long, transaction: WCEthereumTransaction) -> Unit = { _, _ -> Unit } 74 | var onCustomRequest: (id: Long, payload: String) -> Unit = { _, _ -> Unit } 75 | var onBnbTrade: (id: Long, order: WCBinanceTradeOrder) -> Unit = { _, _ -> Unit } 76 | var onBnbCancel: (id: Long, order: WCBinanceCancelOrder) -> Unit = { _, _ -> Unit } 77 | var onBnbTransfer: (id: Long, order: WCBinanceTransferOrder) -> Unit = { _, _ -> Unit } 78 | var onBnbTxConfirm: (id: Long, order: WCBinanceTxConfirmParam) -> Unit = { _, _ -> Unit } 79 | var onGetAccounts: (id: Long) -> Unit = { _ -> Unit } 80 | var onSignTransaction: (id: Long, transaction: WCSignTransaction) -> Unit = {_, _ -> Unit } 81 | var onOktSignTransaction:(id:Long, transaction: WCOKExChainTransaction) -> Unit = {_,_->Unit } 82 | var onOktSendTransaction:(id:Long, transaction: WCOKExChainTransaction) -> Unit = { _, _->Unit } 83 | 84 | override fun onOpen(webSocket: WebSocket, response: Response) { 85 | Log.d(TAG, "<< websocket opened >>") 86 | isConnected = true 87 | 88 | listeners.forEach { it.onOpen(webSocket, response) } 89 | 90 | val session = this.session ?: throw IllegalStateException("session can't be null on connection open") 91 | val peerId = this.peerId ?: throw IllegalStateException("peerId can't be null on connection open") 92 | // The Session.topic channel is used to listen session request messages only. 93 | subscribe(session.topic) 94 | // The peerId channel is used to listen to all messages sent to this httpClient. 95 | subscribe(peerId) 96 | } 97 | 98 | override fun onMessage(webSocket: WebSocket, text: String) { 99 | var decrypted: String? = null 100 | try { 101 | Log.d(TAG, "<== message $text") 102 | decrypted = decryptMessage(text) 103 | Log.d(TAG, "<== decrypted $decrypted") 104 | handleMessage(decrypted) 105 | } catch (e: Exception) { 106 | onFailure(e) 107 | } finally { 108 | listeners.forEach { it.onMessage(webSocket, decrypted ?: text) } 109 | } 110 | } 111 | 112 | override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { 113 | resetState() 114 | onFailure(t) 115 | 116 | listeners.forEach { it.onFailure(webSocket, t, response) } 117 | } 118 | 119 | override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { 120 | Log.d(TAG,"<< websocket closed >>") 121 | 122 | listeners.forEach { it.onClosed(webSocket, code, reason) } 123 | } 124 | 125 | override fun onMessage(webSocket: WebSocket, bytes: ByteString) { 126 | Log.d(TAG,"<== pong") 127 | 128 | listeners.forEach { it.onMessage(webSocket, bytes) } 129 | } 130 | 131 | override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { 132 | Log.d(TAG,"<< closing socket >>") 133 | 134 | resetState() 135 | onDisconnect(code, reason) 136 | 137 | listeners.forEach { it.onClosing(webSocket, code, reason) } 138 | } 139 | 140 | fun connect(session: WCSession, peerMeta: WCPeerMeta, peerId: String = UUID.randomUUID().toString(), remotePeerId: String? = null) { 141 | if (this.session != null && this.session?.topic != session.topic) { 142 | killSession() 143 | } 144 | 145 | this.session = session 146 | this.peerMeta = peerMeta 147 | this.peerId = peerId 148 | this.remotePeerId = remotePeerId 149 | 150 | val request = Request.Builder() 151 | .url(session.bridge) 152 | .build() 153 | 154 | socket = httpClient.newWebSocket(request, this) 155 | } 156 | 157 | fun approveSession(accounts: List, chainId: Int): Boolean { 158 | check(handshakeId > 0) { "handshakeId must be greater than 0 on session approve" } 159 | 160 | val result = WCApproveSessionResponse( 161 | chainId = chainId, 162 | accounts = accounts, 163 | peerId = peerId, 164 | peerMeta = peerMeta 165 | ) 166 | val response = JsonRpcResponse( 167 | id = handshakeId, 168 | result = result 169 | ) 170 | 171 | return encryptAndSend(gson.toJson(response)) 172 | } 173 | 174 | fun updateSession(accounts: List? = null, chainId: Int? = null, approved: Boolean = true): Boolean { 175 | val request = JsonRpcRequest( 176 | id = generateId(), 177 | method = WCMethod.SESSION_UPDATE, 178 | params = listOf( 179 | WCSessionUpdate( 180 | approved = approved, 181 | chainId = chainId, 182 | accounts = accounts 183 | ) 184 | ) 185 | ) 186 | return encryptAndSend(gson.toJson(request)) 187 | } 188 | 189 | fun rejectSession(message: String = "Session rejected"): Boolean { 190 | check(handshakeId > 0) { "handshakeId must be greater than 0 on session reject" } 191 | 192 | val response = JsonRpcErrorResponse( 193 | id = handshakeId, 194 | error = JsonRpcError.serverError( 195 | message = message 196 | ) 197 | ) 198 | return encryptAndSend(gson.toJson(response)) 199 | } 200 | 201 | fun killSession(): Boolean { 202 | updateSession(approved = false) 203 | return disconnect() 204 | } 205 | 206 | fun approveRequest(id: Long, result: T): Boolean { 207 | val response = JsonRpcResponse( 208 | id = id, 209 | result = result 210 | ) 211 | return encryptAndSend(gson.toJson(response)) 212 | } 213 | 214 | fun rejectRequest(id: Long, message: String = "Reject by the user"): Boolean { 215 | val response = JsonRpcErrorResponse( 216 | id = id, 217 | error = JsonRpcError.serverError( 218 | message = message 219 | ) 220 | ) 221 | return encryptAndSend(gson.toJson(response)) 222 | } 223 | 224 | private fun decryptMessage(text: String): String { 225 | val message = gson.fromJson(text) 226 | val encrypted = gson.fromJson(message.payload) 227 | val session = this.session ?: throw IllegalStateException("session can't be null on message receive") 228 | return String(WCCipher.decrypt(encrypted, session.key.hexStringToByteArray()), Charsets.UTF_8) 229 | } 230 | 231 | private fun invalidParams(id: Long): Boolean { 232 | val response = JsonRpcErrorResponse( 233 | id = id, 234 | error = JsonRpcError.invalidParams( 235 | message = "Invalid parameters" 236 | ) 237 | ) 238 | 239 | return encryptAndSend(gson.toJson(response)) 240 | } 241 | 242 | private fun handleMessage(payload: String) { 243 | try { 244 | val request = gson.fromJson>(payload, typeToken>()) 245 | val method = request.method 246 | if (method != null) { 247 | handleRequest(request) 248 | } else { 249 | onCustomRequest(request.id, payload) 250 | } 251 | } catch (e: InvalidJsonRpcParamsException) { 252 | invalidParams(e.requestId) 253 | } 254 | } 255 | 256 | private fun handleRequest(request: JsonRpcRequest) { 257 | when (request.method) { 258 | WCMethod.SESSION_REQUEST -> { 259 | val param = gson.fromJson>(request.params) 260 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 261 | handshakeId = request.id 262 | remotePeerId = param.peerId 263 | onSessionRequest(request.id, param.peerMeta) 264 | } 265 | WCMethod.SESSION_UPDATE -> { 266 | val param = gson.fromJson>(request.params) 267 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 268 | if (!param.approved) { 269 | killSession() 270 | } 271 | } 272 | WCMethod.ETH_SIGN -> { 273 | val params = gson.fromJson>(request.params) 274 | if (params.size < 2) 275 | throw InvalidJsonRpcParamsException(request.id) 276 | onEthSign(request.id, WCEthereumSignMessage(params, WCEthereumSignMessage.WCSignType.MESSAGE)) 277 | } 278 | WCMethod.ETH_PERSONAL_SIGN -> { 279 | val params = gson.fromJson>(request.params) 280 | if (params.size < 2) 281 | throw InvalidJsonRpcParamsException(request.id) 282 | onEthSign(request.id, WCEthereumSignMessage(params, WCEthereumSignMessage.WCSignType.PERSONAL_MESSAGE)) 283 | } 284 | WCMethod.ETH_SIGN_TYPE_DATA -> { 285 | val params = gson.fromJson>(request.params) 286 | if (params.size < 2) 287 | throw InvalidJsonRpcParamsException(request.id) 288 | onEthSign(request.id, WCEthereumSignMessage(params, WCEthereumSignMessage.WCSignType.TYPED_MESSAGE)) 289 | } 290 | WCMethod.ETH_SIGN_TRANSACTION -> { 291 | val param = gson.fromJson>(request.params) 292 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 293 | onEthSignTransaction(request.id, param) 294 | } 295 | WCMethod.ETH_SEND_TRANSACTION ->{ 296 | val param = gson.fromJson>(request.params) 297 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 298 | onEthSendTransaction(request.id, param) 299 | } 300 | WCMethod.BNB_SIGN -> { 301 | try { 302 | val order = gson.fromJson>(request.params) 303 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 304 | onBnbCancel(request.id, order) 305 | } catch (e: NoSuchElementException) { } 306 | 307 | try { 308 | val order = gson.fromJson>(request.params) 309 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 310 | onBnbTrade(request.id, order) 311 | } catch (e: NoSuchElementException) { } 312 | 313 | try { 314 | val order = gson.fromJson>(request.params) 315 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 316 | onBnbTransfer(request.id, order) 317 | } catch (e: NoSuchElementException) { } 318 | } 319 | WCMethod.OKT_SEND_TRANSACTION -> { 320 | val param = gson.fromJson>(request.params) 321 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 322 | onOktSendTransaction(request.id,param) 323 | } 324 | WCMethod.OKT_SIGN_TRANSACTION -> { 325 | val param = gson.fromJson>(request.params) 326 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 327 | onOktSignTransaction(request.id,param) 328 | 329 | } 330 | WCMethod.BNB_TRANSACTION_CONFIRM -> { 331 | val param = gson.fromJson>(request.params) 332 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 333 | onBnbTxConfirm(request.id, param) 334 | } 335 | WCMethod.GET_ACCOUNTS -> { 336 | onGetAccounts(request.id) 337 | } 338 | WCMethod.SIGN_TRANSACTION -> { 339 | val param = gson.fromJson>(request.params) 340 | .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) 341 | onSignTransaction(request.id, param) 342 | } 343 | } 344 | } 345 | 346 | private fun subscribe(topic: String): Boolean { 347 | val message = WCSocketMessage( 348 | topic = topic, 349 | type = MessageType.SUB, 350 | payload = "" 351 | ) 352 | val json = gson.toJson(message) 353 | Log.d(TAG,"==> subscribe $json") 354 | 355 | return socket?.send(gson.toJson(message)) ?: false 356 | } 357 | 358 | private fun encryptAndSend(result: String): Boolean { 359 | Log.d(TAG,"==> message $result") 360 | val session = this.session ?: throw IllegalStateException("session can't be null on message send") 361 | val payload = gson.toJson(WCCipher.encrypt(result.toByteArray(Charsets.UTF_8), session.key.hexStringToByteArray())) 362 | val message = WCSocketMessage( 363 | // Once the remotePeerId is defined, all messages must be sent to this channel. The session.topic channel 364 | // will be used only to respond the session request message. 365 | topic = remotePeerId ?: session.topic, 366 | type = MessageType.PUB, 367 | payload = payload 368 | ) 369 | val json = gson.toJson(message) 370 | Log.d(TAG,"==> encrypted $json") 371 | 372 | return socket?.send(json) ?: false 373 | } 374 | 375 | 376 | fun disconnect(): Boolean { 377 | return socket?.close(WS_CLOSE_NORMAL, null) ?: false 378 | } 379 | 380 | fun addSocketListener(listener: WebSocketListener) { 381 | listeners.add(listener) 382 | } 383 | 384 | fun removeSocketListener(listener: WebSocketListener) { 385 | listeners.remove(listener) 386 | } 387 | 388 | private fun resetState() { 389 | handshakeId = -1 390 | isConnected = false 391 | session = null 392 | peerId = null 393 | remotePeerId = null 394 | peerMeta = null 395 | } 396 | } 397 | 398 | private fun generateId(): Long { 399 | return Date().time 400 | } 401 | --------------------------------------------------------------------------------