diff --git a/walletconnect/.gitignore b/walletconnect/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/walletconnect/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/walletconnect/build.gradle b/walletconnect/build.gradle
new file mode 100644
index 0000000..d2a9d4a
--- /dev/null
+++ b/walletconnect/build.gradle
@@ -0,0 +1,54 @@
+apply plugin: 'com.android.library'
+
+apply plugin: 'kotlin-android'
+
+group='com.github.TrustWallet'
+
+android {
+ compileSdkVersion 28
+
+
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ testOptions {
+ unitTests {
+ includeAndroidResources = true
+ }
+ }
+ compileOptions {
+ sourceCompatibility = "1.8"
+ targetCompatibility = 1.8
+ }
+ buildToolsVersion = '28.0.3'
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'com.squareup.okhttp3:okhttp:4.9.2'
+ implementation 'com.google.code.gson:gson:2.8.7'
+ implementation 'com.github.salomonbrys.kotson:kotson:2.5.0'
+ implementation 'com.android.support:appcompat-v7:28.0.0'
+ testImplementation 'junit:junit:4.13'
+ testImplementation 'org.robolectric:robolectric:4.3'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+}
+
+tasks.withType(Javadoc).all { enabled = false }
+apply from: "$rootDir/publish/publish.gradle"
diff --git a/walletconnect/library.properties b/walletconnect/library.properties
new file mode 100644
index 0000000..5c14cac
--- /dev/null
+++ b/walletconnect/library.properties
@@ -0,0 +1,4 @@
+artifact=walletconnect
+libraryName=com.hipo.macaron
+libraryVersion=1.0.0
+libraryDescription=WalletConnect Library that's been forked from TrustWallet
diff --git a/walletconnect/proguard-rules.pro b/walletconnect/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/walletconnect/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
diff --git a/walletconnect/src/main/AndroidManifest.xml b/walletconnect/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8cf514c
--- /dev/null
+++ b/walletconnect/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/WCCipher.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/WCCipher.kt
new file mode 100644
index 0000000..c2ad167
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/WCCipher.kt
@@ -0,0 +1,73 @@
+package com.trustwallet.walletconnect
+
+import com.trustwallet.walletconnect.exceptions.InvalidHmacException
+import com.trustwallet.walletconnect.extensions.hexStringToByteArray
+import com.trustwallet.walletconnect.extensions.toHex
+import com.trustwallet.walletconnect.models.WCEncryptionPayload
+import java.security.SecureRandom
+import javax.crypto.Cipher
+import javax.crypto.Mac
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+private val CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding"
+private val MAC_ALGORITHM = "HmacSHA256"
+
+object WCCipher {
+ fun encrypt(data: ByteArray, key: ByteArray): WCEncryptionPayload {
+ val iv = randomBytes(16)
+ val keySpec = SecretKeySpec(key, "AES")
+ val ivSpec = IvParameterSpec(iv)
+ val cipher = Cipher.getInstance(CIPHER_ALGORITHM)
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
+
+ val encryptedData = cipher.doFinal(data)
+ val hmac = computeHmac(
+ data = encryptedData,
+ iv = iv,
+ key = key
+ )
+
+ return WCEncryptionPayload(
+ data = encryptedData.toHex(),
+ iv = iv.toHex(),
+ hmac = hmac
+ )
+ }
+
+ fun decrypt(payload: WCEncryptionPayload, key: ByteArray): ByteArray {
+ val data = payload.data.hexStringToByteArray()
+ val iv = payload.iv.hexStringToByteArray()
+ val computedHmac = computeHmac(
+ data = data,
+ iv = iv,
+ key = key
+ )
+
+ if (computedHmac != payload.hmac.toLowerCase()) {
+ throw InvalidHmacException()
+ }
+
+ val keySpec = SecretKeySpec(key, "AES")
+ val ivSpec = IvParameterSpec(iv)
+ val cipher = Cipher.getInstance(CIPHER_ALGORITHM)
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec)
+
+ return cipher.doFinal(data)
+ }
+
+ private fun computeHmac(data: ByteArray, iv: ByteArray, key: ByteArray): String {
+ val mac = Mac.getInstance(MAC_ALGORITHM)
+ val payload = data + iv
+ mac.init(SecretKeySpec(key, MAC_ALGORITHM))
+ return mac.doFinal(payload).toHex()
+ }
+
+ private fun randomBytes(size: Int): ByteArray {
+ val secureRandom = SecureRandom()
+ val bytes = ByteArray(size)
+ secureRandom.nextBytes(bytes)
+
+ return bytes
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/WCClient.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/WCClient.kt
new file mode 100644
index 0000000..0665673
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/WCClient.kt
@@ -0,0 +1,392 @@
+package com.trustwallet.walletconnect
+
+import android.util.Log
+import com.github.salomonbrys.kotson.fromJson
+import com.github.salomonbrys.kotson.registerTypeAdapter
+import com.github.salomonbrys.kotson.typeToken
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonArray
+import com.trustwallet.walletconnect.exceptions.InvalidJsonRpcParamsException
+import com.trustwallet.walletconnect.extensions.hexStringToByteArray
+import com.trustwallet.walletconnect.jsonrpc.JsonRpcError
+import com.trustwallet.walletconnect.jsonrpc.JsonRpcErrorResponse
+import com.trustwallet.walletconnect.jsonrpc.JsonRpcRequest
+import com.trustwallet.walletconnect.jsonrpc.JsonRpcResponse
+import com.trustwallet.walletconnect.models.*
+import com.trustwallet.walletconnect.models.binance.*
+import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
+import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
+import com.trustwallet.walletconnect.models.ethereum.ethTransactionSerializer
+import com.trustwallet.walletconnect.models.session.WCApproveSessionResponse
+import com.trustwallet.walletconnect.models.session.WCSession
+import com.trustwallet.walletconnect.models.session.WCSessionRequest
+import com.trustwallet.walletconnect.models.session.WCSessionUpdate
+import okhttp3.*
+import okio.ByteString
+import java.util.*
+
+const val JSONRPC_VERSION = "2.0"
+const val WS_CLOSE_NORMAL = 1000
+
+open class WCClient (
+ builder: GsonBuilder = GsonBuilder(),
+ private val httpClient: OkHttpClient
+): WebSocketListener() {
+ private val TAG = "WCClient"
+
+ private val gson = builder
+ .serializeNulls()
+ .registerTypeAdapter(cancelOrderSerializer)
+ .registerTypeAdapter(cancelOrderDeserializer)
+ .registerTypeAdapter(tradeOrderSerializer)
+ .registerTypeAdapter(tradeOrderDeserializer)
+ .registerTypeAdapter(transferOrderSerializer)
+ .registerTypeAdapter(transferOrderDeserializer)
+ .registerTypeAdapter(ethTransactionSerializer)
+ .create()
+
+ private var socket: WebSocket? = null
+
+ private val listeners: MutableSet = mutableSetOf()
+
+ var session: WCSession? = null
+ private set
+
+ var peerMeta: WCPeerMeta? = null
+ private set
+
+ var peerId: String? = null
+ private set
+
+ var remotePeerId: String? = null
+ private set
+
+ var chainId: String? = null
+ private set
+
+ var isConnected: Boolean = false
+ private set
+
+ private var handshakeId: Long = -1
+
+ var onFailure: (Throwable) -> Unit = { _ -> Unit}
+ var onDisconnect: (code: Int, reason: String) -> Unit = { _, _ -> Unit }
+ var onSessionRequest: (id: Long, peer: WCPeerMeta) -> Unit = { _, _ -> Unit }
+ var onEthSign: (id: Long, message: WCEthereumSignMessage) -> Unit = { _, _ -> Unit }
+ var onEthSignTransaction: (id: Long, transaction: WCEthereumTransaction) -> Unit = { _, _ -> Unit }
+ var onEthSendTransaction: (id: Long, transaction: WCEthereumTransaction) -> Unit = { _, _ -> Unit }
+ var onCustomRequest: (id: Long, payload: String) -> Unit = { _, _ -> Unit }
+ var onBnbTrade: (id: Long, order: WCBinanceTradeOrder) -> Unit = { _, _ -> Unit }
+ var onBnbCancel: (id: Long, order: WCBinanceCancelOrder) -> Unit = { _, _ -> Unit }
+ var onBnbTransfer: (id: Long, order: WCBinanceTransferOrder) -> Unit = { _, _ -> Unit }
+ var onBnbTxConfirm: (id: Long, order: WCBinanceTxConfirmParam) -> Unit = { _, _ -> Unit }
+ var onGetAccounts: (id: Long) -> Unit = { _ -> Unit }
+ var onSignTransaction: (id: Long, transaction: WCSignTransaction) -> Unit = {_, _ -> Unit }
+
+ override fun onOpen(webSocket: WebSocket, response: Response) {
+ Log.d(TAG, "<< websocket opened >>")
+ isConnected = true
+
+ listeners.forEach { it.onOpen(webSocket, response) }
+
+ val session = this.session ?: throw IllegalStateException("session can't be null on connection open")
+ val peerId = this.peerId ?: throw IllegalStateException("peerId can't be null on connection open")
+ // The Session.topic channel is used to listen session request messages only.
+ subscribe(session.topic)
+ // The peerId channel is used to listen to all messages sent to this httpClient.
+ subscribe(peerId)
+ }
+
+ override fun onMessage(webSocket: WebSocket, text: String) {
+ var decrypted: String? = null
+ try {
+ Log.d(TAG, "<== message $text")
+ decrypted = decryptMessage(text)
+ Log.d(TAG, "<== decrypted $decrypted")
+ handleMessage(decrypted)
+ } catch (e: Exception) {
+ onFailure(e)
+ } finally {
+ listeners.forEach { it.onMessage(webSocket, decrypted ?: text) }
+ }
+ }
+
+ override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
+ resetState()
+ onFailure(t)
+
+ listeners.forEach { it.onFailure(webSocket, t, response) }
+ }
+
+ override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
+ Log.d(TAG,"<< websocket closed >>")
+
+ listeners.forEach { it.onClosed(webSocket, code, reason) }
+ }
+
+ override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
+ Log.d(TAG,"<== pong")
+
+ listeners.forEach { it.onMessage(webSocket, bytes) }
+ }
+
+ override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
+ Log.d(TAG,"<< closing socket >>")
+
+ resetState()
+ onDisconnect(code, reason)
+
+ listeners.forEach { it.onClosing(webSocket, code, reason) }
+ }
+
+ fun connect(session: WCSession, peerMeta: WCPeerMeta, peerId: String = UUID.randomUUID().toString(), remotePeerId: String? = null) {
+ if (this.session != null && this.session?.topic != session.topic) {
+ killSession()
+ }
+
+ this.session = session
+ this.peerMeta = peerMeta
+ this.peerId = peerId
+ this.remotePeerId = remotePeerId
+
+ val request = Request.Builder()
+ .url(session.bridge)
+ .build()
+
+ socket = httpClient.newWebSocket(request, this)
+ }
+
+ fun approveSession(accounts: List, chainId: Int): Boolean {
+ check(handshakeId > 0) { "handshakeId must be greater than 0 on session approve" }
+
+ val result = WCApproveSessionResponse(
+ chainId = this.chainId?.toIntOrNull() ?: chainId,
+ accounts = accounts,
+ peerId = peerId,
+ peerMeta = peerMeta
+ )
+ val response = JsonRpcResponse(
+ id = handshakeId,
+ result = result
+ )
+
+ return encryptAndSend(gson.toJson(response))
+ }
+
+ fun updateSession(accounts: List? = null, chainId: Int? = null, approved: Boolean = true): Boolean {
+ val request = JsonRpcRequest(
+ id = generateId(),
+ method = WCMethod.SESSION_UPDATE,
+ params = listOf(
+ WCSessionUpdate(
+ approved = approved,
+ chainId = this.chainId?.toIntOrNull() ?: chainId,
+ accounts = accounts
+ )
+ )
+ )
+ return encryptAndSend(gson.toJson(request))
+ }
+
+ fun rejectSession(message: String = "Session rejected"): Boolean {
+ check(handshakeId > 0) { "handshakeId must be greater than 0 on session reject" }
+
+ val response = JsonRpcErrorResponse(
+ id = handshakeId,
+ error = JsonRpcError.serverError(
+ message = message
+ )
+ )
+ return encryptAndSend(gson.toJson(response))
+ }
+
+ fun killSession(): Boolean {
+ updateSession(approved = false)
+ return disconnect()
+ }
+
+ fun approveRequest(id: Long, result: T): Boolean {
+ val response = JsonRpcResponse(
+ id = id,
+ result = result
+ )
+ return encryptAndSend(gson.toJson(response))
+ }
+
+ fun rejectRequest(id: Long, message: String = "Reject by the user"): Boolean {
+ val response = JsonRpcErrorResponse(
+ id = id,
+ error = JsonRpcError.serverError(
+ message = message
+ )
+ )
+ return encryptAndSend(gson.toJson(response))
+ }
+
+ private fun decryptMessage(text: String): String {
+ val message = gson.fromJson(text)
+ val encrypted = gson.fromJson(message.payload)
+ val session = this.session ?: throw IllegalStateException("session can't be null on message receive")
+ return String(WCCipher.decrypt(encrypted, session.key.hexStringToByteArray()), Charsets.UTF_8)
+ }
+
+ private fun invalidParams(id: Long): Boolean {
+ val response = JsonRpcErrorResponse(
+ id = id,
+ error = JsonRpcError.invalidParams(
+ message = "Invalid parameters"
+ )
+ )
+
+ return encryptAndSend(gson.toJson(response))
+ }
+
+ private fun handleMessage(payload: String) {
+ try {
+ val request = gson.fromJson>(payload, typeToken>())
+ val method = request.method
+ if (method != null) {
+ handleRequest(request)
+ } else {
+ onCustomRequest(request.id, payload)
+ }
+ } catch (e: InvalidJsonRpcParamsException) {
+ invalidParams(e.requestId)
+ }
+ }
+
+ private fun handleRequest(request: JsonRpcRequest) {
+ when (request.method) {
+ WCMethod.SESSION_REQUEST -> {
+ val param = gson.fromJson>(request.params)
+ .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id)
+ handshakeId = request.id
+ remotePeerId = param.peerId
+ chainId = param.chainId
+ onSessionRequest(request.id, param.peerMeta)
+ }
+ WCMethod.SESSION_UPDATE -> {
+ val param = gson.fromJson>(request.params)
+ .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id)
+ if (!param.approved) {
+ killSession()
+ }
+ }
+ WCMethod.ETH_SIGN -> {
+ val params = gson.fromJson>(request.params)
+ if (params.size < 2)
+ throw InvalidJsonRpcParamsException(request.id)
+ onEthSign(request.id, WCEthereumSignMessage(params, WCEthereumSignMessage.WCSignType.MESSAGE))
+ }
+ WCMethod.ETH_PERSONAL_SIGN -> {
+ val params = gson.fromJson>(request.params)
+ if (params.size < 2)
+ throw InvalidJsonRpcParamsException(request.id)
+ onEthSign(request.id, WCEthereumSignMessage(params, WCEthereumSignMessage.WCSignType.PERSONAL_MESSAGE))
+ }
+ WCMethod.ETH_SIGN_TYPE_DATA -> {
+ val params = gson.fromJson>(request.params)
+ if (params.size < 2)
+ throw InvalidJsonRpcParamsException(request.id)
+ onEthSign(request.id, WCEthereumSignMessage(params, WCEthereumSignMessage.WCSignType.TYPED_MESSAGE))
+ }
+ WCMethod.ETH_SIGN_TRANSACTION -> {
+ val param = gson.fromJson>(request.params)
+ .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id)
+ onEthSignTransaction(request.id, param)
+ }
+ WCMethod.ETH_SEND_TRANSACTION ->{
+ val param = gson.fromJson>(request.params)
+ .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id)
+ onEthSendTransaction(request.id, param)
+ }
+ WCMethod.BNB_SIGN -> {
+ try {
+ val order = gson.fromJson>(request.params)
+ .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id)
+ onBnbCancel(request.id, order)
+ } catch (e: NoSuchElementException) { }
+
+ try {
+ val order = gson.fromJson>(request.params)
+ .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id)
+ onBnbTrade(request.id, order)
+ } catch (e: NoSuchElementException) { }
+
+ try {
+ val order = gson.fromJson>(request.params)
+ .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id)
+ onBnbTransfer(request.id, order)
+ } catch (e: NoSuchElementException) { }
+ }
+ WCMethod.BNB_TRANSACTION_CONFIRM -> {
+ val param = gson.fromJson>(request.params)
+ .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id)
+ onBnbTxConfirm(request.id, param)
+ }
+ WCMethod.GET_ACCOUNTS -> {
+ onGetAccounts(request.id)
+ }
+ WCMethod.SIGN_TRANSACTION -> {
+ val param = gson.fromJson>(request.params)
+ .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id)
+ onSignTransaction(request.id, param)
+ }
+ }
+ }
+
+ private fun subscribe(topic: String): Boolean {
+ val message = WCSocketMessage(
+ topic = topic,
+ type = MessageType.SUB,
+ payload = ""
+ )
+ val json = gson.toJson(message)
+ Log.d(TAG,"==> subscribe $json")
+
+ return socket?.send(gson.toJson(message)) ?: false
+ }
+
+ private fun encryptAndSend(result: String): Boolean {
+ Log.d(TAG,"==> message $result")
+ val session = this.session ?: throw IllegalStateException("session can't be null on message send")
+ val payload = gson.toJson(WCCipher.encrypt(result.toByteArray(Charsets.UTF_8), session.key.hexStringToByteArray()))
+ val message = WCSocketMessage(
+ // Once the remotePeerId is defined, all messages must be sent to this channel. The session.topic channel
+ // will be used only to respond the session request message.
+ topic = remotePeerId ?: session.topic,
+ type = MessageType.PUB,
+ payload = payload
+ )
+ val json = gson.toJson(message)
+ Log.d(TAG,"==> encrypted $json")
+
+ return socket?.send(json) ?: false
+ }
+
+
+ fun disconnect(): Boolean {
+ return socket?.close(WS_CLOSE_NORMAL, null) ?: false
+ }
+
+ fun addSocketListener(listener: WebSocketListener) {
+ listeners.add(listener)
+ }
+
+ fun removeSocketListener(listener: WebSocketListener) {
+ listeners.remove(listener)
+ }
+
+ private fun resetState() {
+ handshakeId = -1
+ isConnected = false
+ session = null
+ peerId = null
+ remotePeerId = null
+ peerMeta = null
+ }
+}
+
+private fun generateId(): Long {
+ return Date().time
+}
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/WCSessionStore.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/WCSessionStore.kt
new file mode 100644
index 0000000..c711851
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/WCSessionStore.kt
@@ -0,0 +1,19 @@
+package com.trustwallet.walletconnect
+
+import com.trustwallet.walletconnect.models.WCPeerMeta
+import com.trustwallet.walletconnect.models.session.WCSession
+import java.util.*
+
+data class WCSessionStoreItem(
+ val session: WCSession,
+ val chainId: Int,
+ val peerId: String,
+ val remotePeerId: String,
+ val remotePeerMeta: WCPeerMeta,
+ val isAutoSign: Boolean = false,
+ val date: Date = Date()
+)
+
+interface WCSessionStore {
+ var session: WCSessionStoreItem?
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/WCSessionStoreType.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/WCSessionStoreType.kt
new file mode 100644
index 0000000..e988840
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/WCSessionStoreType.kt
@@ -0,0 +1,35 @@
+package com.trustwallet.walletconnect
+
+import android.content.SharedPreferences
+import com.github.salomonbrys.kotson.*
+import com.google.gson.GsonBuilder
+
+class WCSessionStoreType(
+ private val sharedPreferences: SharedPreferences,
+ builder: GsonBuilder = GsonBuilder()
+): WCSessionStore {
+ private val gson = builder
+ .serializeNulls()
+ .create()
+
+ private fun store(item: WCSessionStoreItem?) {
+ if (item != null) {
+ sharedPreferences.edit().putString(SESSION_KEY, gson.toJson(item)).apply()
+ } else {
+ sharedPreferences.edit().remove(SESSION_KEY).apply()
+ }
+ }
+
+ private fun load(): WCSessionStoreItem? {
+ val json = sharedPreferences.getString(SESSION_KEY, null) ?: return null
+ return gson.fromJson(json)
+ }
+
+ override var session: WCSessionStoreItem?
+ set(item) = store(item)
+ get() = load()
+
+ companion object {
+ private const val SESSION_KEY = "org.walletconnect.session"
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidHmacException.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidHmacException.kt
new file mode 100644
index 0000000..f6215f8
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidHmacException.kt
@@ -0,0 +1,5 @@
+package com.trustwallet.walletconnect.exceptions
+
+import java.lang.Exception
+
+class InvalidHmacException : Exception("Received and computed HMAC doesn't mach")
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidJsonRpcParamsException.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidJsonRpcParamsException.kt
new file mode 100644
index 0000000..1624b4d
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidJsonRpcParamsException.kt
@@ -0,0 +1,5 @@
+package com.trustwallet.walletconnect.exceptions
+
+import java.lang.Exception
+
+class InvalidJsonRpcParamsException(val requestId: Long) : Exception("Invalid JSON RPC Request")
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidMessageException.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidMessageException.kt
new file mode 100644
index 0000000..9888268
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidMessageException.kt
@@ -0,0 +1,5 @@
+package com.trustwallet.walletconnect.exceptions
+
+import java.lang.Exception
+
+class InvalidMessageException : Exception("Invalid message")
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidPayloadException.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidPayloadException.kt
new file mode 100644
index 0000000..9805467
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidPayloadException.kt
@@ -0,0 +1,5 @@
+package com.trustwallet.walletconnect.exceptions
+
+import java.lang.Exception
+
+class InvalidPayloadException : Exception("Invalid WCEncryptionPayload")
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidSessionException.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidSessionException.kt
new file mode 100644
index 0000000..8c38512
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidSessionException.kt
@@ -0,0 +1,5 @@
+package com.trustwallet.walletconnect.exceptions
+
+import java.lang.Exception
+
+class InvalidSessionException : Exception("Invalid session")
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidUriException.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidUriException.kt
new file mode 100644
index 0000000..8d3fb55
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/InvalidUriException.kt
@@ -0,0 +1,5 @@
+package com.trustwallet.walletconnect.exceptions
+
+import java.lang.Exception
+
+class InvalidUriException : Exception("Invalid Wallet Connect URI")
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/RequiredFieldException.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/RequiredFieldException.kt
new file mode 100644
index 0000000..093a5b8
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/exceptions/RequiredFieldException.kt
@@ -0,0 +1,5 @@
+package com.trustwallet.walletconnect.exceptions
+
+import com.google.gson.JsonParseException
+
+class RequiredFieldException(val field: String = "") : JsonParseException("'$field' is required")
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/extensions/ByteArray.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/extensions/ByteArray.kt
new file mode 100644
index 0000000..e2797d8
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/extensions/ByteArray.kt
@@ -0,0 +1,17 @@
+package com.trustwallet.walletconnect.extensions
+
+private val HEX_CHARS = "0123456789abcdef".toCharArray()
+
+fun ByteArray.toHex() : String{
+ val result = StringBuffer()
+
+ forEach {
+ val octet = it.toInt()
+ val firstIndex = (octet and 0xF0).ushr(4)
+ val secondIndex = octet and 0x0F
+ result.append(HEX_CHARS[firstIndex])
+ result.append(HEX_CHARS[secondIndex])
+ }
+
+ return result.toString()
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/extensions/String.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/extensions/String.kt
new file mode 100644
index 0000000..88e4d8b
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/extensions/String.kt
@@ -0,0 +1,18 @@
+package com.trustwallet.walletconnect.extensions
+
+private val HEX_CHARS = "0123456789abcdef"
+
+fun String.hexStringToByteArray() : ByteArray {
+ val hex = toLowerCase()
+ val result = ByteArray(length / 2)
+
+ for (i in 0 until hex.length step 2) {
+ val firstIndex = HEX_CHARS.indexOf(hex[i])
+ val secondIndex = HEX_CHARS.indexOf(hex[i + 1])
+
+ val octet = firstIndex.shl(4).or(secondIndex)
+ result.set(i.shr(1), octet.toByte())
+ }
+
+ return result
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcError.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcError.kt
new file mode 100644
index 0000000..d87a7d7
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcError.kt
@@ -0,0 +1,14 @@
+package com.trustwallet.walletconnect.jsonrpc
+
+data class JsonRpcError (
+ val code: Int,
+ val message: String
+) {
+ companion object {
+ fun serverError(message: String) = JsonRpcError(-32000, message)
+ fun invalidParams(message: String) = JsonRpcError(-32602, message)
+ fun invalidRequest(message: String) = JsonRpcError(-32600, message)
+ fun parseError(message: String) = JsonRpcError(-32700, message)
+ fun methodNotFound(message: String) = JsonRpcError(-32601, message)
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcRequest.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcRequest.kt
new file mode 100644
index 0000000..5a46c73
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcRequest.kt
@@ -0,0 +1,11 @@
+package com.trustwallet.walletconnect.jsonrpc
+
+import com.trustwallet.walletconnect.JSONRPC_VERSION
+import com.trustwallet.walletconnect.models.WCMethod
+
+data class JsonRpcRequest(
+ val id: Long,
+ val jsonrpc: String = JSONRPC_VERSION,
+ val method: WCMethod?,
+ val params: T
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcResponse.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcResponse.kt
new file mode 100644
index 0000000..55a2ea1
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/jsonrpc/JsonRpcResponse.kt
@@ -0,0 +1,15 @@
+package com.trustwallet.walletconnect.jsonrpc
+
+import com.trustwallet.walletconnect.JSONRPC_VERSION
+
+data class JsonRpcResponse (
+ val jsonrpc: String = JSONRPC_VERSION,
+ val id: Long,
+ val result: T
+)
+
+data class JsonRpcErrorResponse (
+ val jsonrpc: String = JSONRPC_VERSION,
+ val id: Long,
+ val error: JsonRpcError
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/MessageType.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/MessageType.kt
new file mode 100644
index 0000000..179c76e
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/MessageType.kt
@@ -0,0 +1,8 @@
+package com.trustwallet.walletconnect.models
+
+import com.google.gson.annotations.SerializedName
+
+enum class MessageType {
+ @SerializedName("pub") PUB,
+ @SerializedName("sub") SUB
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCAccount.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCAccount.kt
new file mode 100644
index 0000000..d99d839
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCAccount.kt
@@ -0,0 +1,6 @@
+package com.trustwallet.walletconnect.models
+
+data class WCAccount(
+ val network: Int,
+ val address: String
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCEncryptionPayload.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCEncryptionPayload.kt
new file mode 100644
index 0000000..fdd71c6
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCEncryptionPayload.kt
@@ -0,0 +1,7 @@
+package com.trustwallet.walletconnect.models
+
+data class WCEncryptionPayload(
+ val data: String,
+ val hmac: String,
+ val iv: String
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCMethod.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCMethod.kt
new file mode 100644
index 0000000..5880bfd
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCMethod.kt
@@ -0,0 +1,38 @@
+package com.trustwallet.walletconnect.models
+
+import com.google.gson.annotations.SerializedName
+
+enum class WCMethod {
+ @SerializedName("wc_sessionRequest")
+ SESSION_REQUEST,
+
+ @SerializedName("wc_sessionUpdate")
+ SESSION_UPDATE,
+
+ @SerializedName("eth_sign")
+ ETH_SIGN,
+
+ @SerializedName("personal_sign")
+ ETH_PERSONAL_SIGN,
+
+ @SerializedName("eth_signTypedData")
+ ETH_SIGN_TYPE_DATA,
+
+ @SerializedName("eth_signTransaction")
+ ETH_SIGN_TRANSACTION,
+
+ @SerializedName("eth_sendTransaction")
+ ETH_SEND_TRANSACTION,
+
+ @SerializedName("bnb_sign")
+ BNB_SIGN,
+
+ @SerializedName("bnb_tx_confirmation")
+ BNB_TRANSACTION_CONFIRM,
+
+ @SerializedName("get_accounts")
+ GET_ACCOUNTS,
+
+ @SerializedName("trust_signTransaction")
+ SIGN_TRANSACTION;
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCPeerMeta.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCPeerMeta.kt
new file mode 100644
index 0000000..1682648
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCPeerMeta.kt
@@ -0,0 +1,8 @@
+package com.trustwallet.walletconnect.models
+
+data class WCPeerMeta (
+ val name: String,
+ val url: String,
+ val description: String? = null,
+ val icons: List = listOf("")
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCSignTransaction.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCSignTransaction.kt
new file mode 100644
index 0000000..54aba46
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCSignTransaction.kt
@@ -0,0 +1,6 @@
+package com.trustwallet.walletconnect.models
+
+data class WCSignTransaction(
+ val network: Int,
+ val transaction: String
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCSocketMessage.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCSocketMessage.kt
new file mode 100644
index 0000000..edbacdc
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/WCSocketMessage.kt
@@ -0,0 +1,7 @@
+package com.trustwallet.walletconnect.models
+
+data class WCSocketMessage(
+ val topic: String,
+ val type: MessageType,
+ val payload: String
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceCancelOrder.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceCancelOrder.kt
new file mode 100644
index 0000000..19bddf5
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceCancelOrder.kt
@@ -0,0 +1,46 @@
+package com.trustwallet.walletconnect.models.binance
+
+import com.github.salomonbrys.kotson.get
+import com.github.salomonbrys.kotson.jsonDeserializer
+import com.github.salomonbrys.kotson.jsonSerializer
+import com.github.salomonbrys.kotson.string
+import com.google.gson.JsonObject
+
+class WCBinanceCancelOrder(
+ account_number: String,
+ chain_id: String,
+ data: String?,
+ memo: String?,
+ sequence: String,
+ source: String,
+ msgs: List
+): WCBinanceOrder(account_number, chain_id, data, memo, sequence, source, msgs) {
+
+ enum class MessageKey(val key: String) {
+ REFID("refid"),
+ SENDER("sender"),
+ SYMBOL("symbol")
+ }
+
+ data class Message(
+ val refid: String,
+ val sender: String,
+ val symbol: String
+ )
+}
+
+val cancelOrderDeserializer = jsonDeserializer {
+ WCBinanceCancelOrder.Message(
+ refid = it.json[WCBinanceCancelOrder.MessageKey.REFID.key].string,
+ sender = it.json[WCBinanceCancelOrder.MessageKey.SENDER.key].string,
+ symbol = it.json[WCBinanceCancelOrder.MessageKey.SYMBOL.key].string
+ )
+}
+
+val cancelOrderSerializer = jsonSerializer {
+ val jsonObject = JsonObject()
+ jsonObject.addProperty("refid", it.src.refid)
+ jsonObject.addProperty("sender", it.src.sender)
+ jsonObject.addProperty("symbol", it.src.symbol)
+ jsonObject
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceOrder.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceOrder.kt
new file mode 100644
index 0000000..291ba2b
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceOrder.kt
@@ -0,0 +1,20 @@
+package com.trustwallet.walletconnect.models.binance
+
+import com.google.gson.annotations.SerializedName
+
+open class WCBinanceOrder(
+ @SerializedName("account_number")
+ val accountNumber: String,
+ @SerializedName("chain_id")
+ val chainId: String,
+ val data: String?,
+ val memo: String?,
+ val sequence: String,
+ val source: String,
+ val msgs: List
+)
+
+data class WCBinanceTxConfirmParam(
+ val ok: Boolean,
+ val errorMsg: String?
+)
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradeOrder.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradeOrder.kt
new file mode 100644
index 0000000..3a29cd7
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradeOrder.kt
@@ -0,0 +1,64 @@
+package com.trustwallet.walletconnect.models.binance
+
+import com.github.salomonbrys.kotson.*
+import com.google.gson.JsonObject
+
+class WCBinanceTradeOrder(
+ account_number: String,
+ chain_id: String,
+ data: String?,
+ memo: String?,
+ sequence: String,
+ source: String,
+ msgs: List
+) : WCBinanceOrder (account_number, chain_id, data, memo, sequence, source, msgs) {
+
+ enum class MessageKey(val key: String) {
+ ID("id"),
+ ORDER_TYPE("ordertype"),
+ PRICE("price"),
+ QUANTITY("quantity"),
+ SENDER("sender"),
+ SIDE("side"),
+ SYMBOL("symbol"),
+ TIME_INFORCE("timeinforce")
+ }
+
+ data class Message(
+ val id: String,
+ val orderType: Int,
+ val price: Long,
+ val quantity: Long,
+ val sender: String,
+ val side: Int,
+ val symbol: String,
+ val timeInforce: Int
+ )
+}
+
+val tradeOrderDeserializer = jsonDeserializer {
+ WCBinanceTradeOrder.Message(
+ id = it.json[WCBinanceTradeOrder.MessageKey.ID.key].string,
+ orderType = it.json[WCBinanceTradeOrder.MessageKey.ORDER_TYPE.key].int,
+ price = it.json[WCBinanceTradeOrder.MessageKey.PRICE.key].long,
+ quantity = it.json[WCBinanceTradeOrder.MessageKey.QUANTITY.key].long,
+ sender = it.json[WCBinanceTradeOrder.MessageKey.SENDER.key].string,
+ side = it.json[WCBinanceTradeOrder.MessageKey.SIDE.key].int,
+ symbol = it.json[WCBinanceTradeOrder.MessageKey.SYMBOL.key].string,
+ timeInforce = it.json[WCBinanceTradeOrder.MessageKey.TIME_INFORCE.key].int
+ )
+}
+
+val tradeOrderSerializer = jsonSerializer {
+ val jsonObject = JsonObject()
+ jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.ID.key, it.src.id)
+ jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.ORDER_TYPE.key, it.src.orderType)
+ jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.PRICE.key, it.src.price)
+ jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.QUANTITY.key, it.src.quantity)
+ jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.SENDER.key, it.src.sender)
+ jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.SIDE.key, it.src.side)
+ jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.SYMBOL.key, it.src.symbol)
+ jsonObject.addProperty(WCBinanceTradeOrder.MessageKey.TIME_INFORCE.key, it.src.timeInforce)
+
+ jsonObject
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradePair.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradePair.kt
new file mode 100644
index 0000000..c8246c9
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradePair.kt
@@ -0,0 +1,17 @@
+package com.trustwallet.walletconnect.models.binance
+
+data class WCBinanceTradePair(val from: String, val to: String) {
+ companion object {
+ fun from(symbol: String): WCBinanceTradePair? {
+ val pair = symbol.split("_")
+
+ return if (pair.size > 1) {
+ val firstParts = pair[0].split("-")
+ val secondParts = pair[1].split("-")
+ WCBinanceTradePair(firstParts[0], secondParts[0])
+ } else {
+ null
+ }
+ }
+ }
+}
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTransferOrder.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTransferOrder.kt
new file mode 100644
index 0000000..8c8a75b
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/binance/WCBinanceTransferOrder.kt
@@ -0,0 +1,51 @@
+package com.trustwallet.walletconnect.models.binance
+
+import com.github.salomonbrys.kotson.*
+import com.google.gson.JsonObject
+
+class WCBinanceTransferOrder(
+ account_number: String,
+ chain_id: String,
+ data: String?,
+ memo: String?,
+ sequence: String,
+ source: String,
+ msgs: List
+): WCBinanceOrder(account_number, chain_id, data, memo, sequence, source, msgs) {
+
+ enum class MessageKey(val key: String) {
+ INPUTS("inputs"),
+ OUTPUTS("outputs")
+ }
+
+ data class Message(
+ val inputs: List- ,
+ val outputs: List
-
+ ) {
+
+ data class Item(
+ val address: String,
+ val coins: List
+ ) {
+
+ data class Coin(
+ val amount: Long,
+ val denom: String
+ )
+ }
+ }
+}
+
+val transferOrderDeserializer = jsonDeserializer {
+ WCBinanceTransferOrder.Message(
+ inputs = it.context.deserialize(it.json[WCBinanceTransferOrder.MessageKey.INPUTS.key].array),
+ outputs = it.context.deserialize(it.json[WCBinanceTransferOrder.MessageKey.OUTPUTS.key].array)
+ )
+}
+
+val transferOrderSerializer = jsonSerializer {
+ val jsonObject = JsonObject()
+ jsonObject.addProperty(WCBinanceTransferOrder.MessageKey.INPUTS.key, it.context.serialize(it.src.inputs))
+ jsonObject.addProperty(WCBinanceTransferOrder.MessageKey.OUTPUTS.key, it.context.serialize(it.src.outputs))
+ jsonObject
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumSignMessage.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumSignMessage.kt
new file mode 100644
index 0000000..642337b
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumSignMessage.kt
@@ -0,0 +1,26 @@
+package com.trustwallet.walletconnect.models.ethereum
+
+data class WCEthereumSignMessage (
+ val raw: List,
+ val type: WCSignType
+) {
+ enum class WCSignType {
+ MESSAGE, PERSONAL_MESSAGE, TYPED_MESSAGE
+ }
+
+ /**
+ * Raw parameters will always be the message and the addess. Depending on the WCSignType,
+ * those parameters can be swapped as description below:
+ *
+ * - MESSAGE: `[address, data ]`
+ * - TYPED_MESSAGE: `[address, data]`
+ * - PERSONAL_MESSAGE: `[data, address]`
+ *
+ * reference: https://docs.walletconnect.org/json-rpc/ethereum#eth_signtypeddata
+ */
+ val data get() = when (type) {
+ WCSignType.MESSAGE -> raw[1]
+ WCSignType.TYPED_MESSAGE -> raw[1]
+ WCSignType.PERSONAL_MESSAGE -> raw[0]
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumTransaction.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumTransaction.kt
new file mode 100644
index 0000000..a7c1909
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumTransaction.kt
@@ -0,0 +1,24 @@
+package com.trustwallet.walletconnect.models.ethereum
+
+import com.github.salomonbrys.kotson.jsonDeserializer
+
+data class WCEthereumTransaction(
+ val from: String,
+ val to: String?,
+ val nonce: String?,
+ val gasPrice: String?,
+ val gas: String?,
+ val gasLimit: String?,
+ val value: String?,
+ val data: String
+)
+
+val ethTransactionSerializer = jsonDeserializer
> {
+ val array = mutableListOf()
+ it.json.asJsonArray.forEach { tx ->
+ if (tx.isJsonObject) {
+ array.add(it.context.deserialize(tx))
+ }
+ }
+ array
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCApproveSessionResponse.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCApproveSessionResponse.kt
new file mode 100644
index 0000000..ecf2db7
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCApproveSessionResponse.kt
@@ -0,0 +1,11 @@
+package com.trustwallet.walletconnect.models.session
+
+import com.trustwallet.walletconnect.models.WCPeerMeta
+
+data class WCApproveSessionResponse(
+ val approved: Boolean = true,
+ val chainId: Int,
+ val accounts: List,
+ val peerId: String?,
+ val peerMeta: WCPeerMeta?
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSession.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSession.kt
new file mode 100644
index 0000000..e39c71d
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSession.kt
@@ -0,0 +1,33 @@
+package com.trustwallet.walletconnect.models.session
+
+import android.net.Uri
+
+data class WCSession (
+ val topic: String,
+ val version: String,
+ val bridge: String,
+ val key: String
+) {
+ fun toUri(): String = "wc:${topic}@${version}?bridge=${bridge}&key=${key}"
+
+ companion object {
+ fun from(from: String): WCSession? {
+ if (!from.startsWith("wc:")) {
+ return null
+ }
+
+ val uriString = from.replace("wc:", "wc://")
+ val uri = Uri.parse(uriString)
+ val bridge = uri.getQueryParameter("bridge")
+ val key = uri.getQueryParameter("key")
+ val topic = uri.userInfo
+ val version = uri.host
+
+ if (bridge == null || key == null || topic == null || version == null) {
+ return null
+ }
+
+ return WCSession(topic, version, bridge, key)
+ }
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSessionRequest.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSessionRequest.kt
new file mode 100644
index 0000000..875e479
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSessionRequest.kt
@@ -0,0 +1,9 @@
+package com.trustwallet.walletconnect.models.session
+
+import com.trustwallet.walletconnect.models.WCPeerMeta
+
+data class WCSessionRequest(
+ val peerId: String,
+ val peerMeta: WCPeerMeta,
+ val chainId: String?
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSessionUpdate.kt b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSessionUpdate.kt
new file mode 100644
index 0000000..3c1137a
--- /dev/null
+++ b/walletconnect/src/main/java/com/trustwallet/walletconnect/models/session/WCSessionUpdate.kt
@@ -0,0 +1,7 @@
+package com.trustwallet.walletconnect.models.session
+
+data class WCSessionUpdate(
+ val approved: Boolean,
+ val chainId: Int?,
+ val accounts: List?
+)
\ No newline at end of file
diff --git a/walletconnect/src/main/res/values/strings.xml b/walletconnect/src/main/res/values/strings.xml
new file mode 100644
index 0000000..eb53a04
--- /dev/null
+++ b/walletconnect/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ WalletConnect
+
diff --git a/walletconnect/src/test/java/com/trustwallet/walletconnect/WCCipherTests.kt b/walletconnect/src/test/java/com/trustwallet/walletconnect/WCCipherTests.kt
new file mode 100644
index 0000000..d5c91be
--- /dev/null
+++ b/walletconnect/src/test/java/com/trustwallet/walletconnect/WCCipherTests.kt
@@ -0,0 +1,40 @@
+package com.trustwallet.walletconnect
+
+import android.os.Build
+import com.trustwallet.walletconnect.extensions.hexStringToByteArray
+import com.trustwallet.walletconnect.models.WCEncryptionPayload
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [Build.VERSION_CODES.O_MR1])
+class WCCipherTests {
+ @Test
+ fun test_decrypt() {
+ val data = "1b3db3674de082d65455eba0ae61cfe7e681c8ef1132e60c8dbd8e52daf18f4fea42cc76366c83351dab6dca52682ff81f828753f89a21e1cc46587ca51ccd353914ffdd3b0394acfee392be6c22b3db9237d3f717a3777e3577dd70408c089a4c9c85130a68c43b0a8aadb00f1b8a8558798104e67aa4ff027b35d4b989e7fd3988d5dcdd563105767670be735b21c4"
+ val hmac = "a33f868e793ca4fcca964bcb64430f65e2f1ca7a779febeaf94c5373d6df48b3"
+ val iv = "89ef1d6728bac2f1dcde2ef9330d2bb8"
+ val key = "5caa3a74154cee16bd1b570a1330be46e086474ac2f4720530662ef1a469662c".hexStringToByteArray()
+ val payload = WCEncryptionPayload(
+ data = data,
+ iv = iv,
+ hmac = hmac
+ )
+
+ val decrypted = String(WCCipher.decrypt(payload, key), Charsets.UTF_8)
+ val expected = "{\"id\":1554098597199736,\"jsonrpc\":\"2.0\",\"method\":\"wc_sessionUpdate\",\"params\":[{\"approved\":false,\"chainId\":null,\"accounts\":null}]}"
+ Assert.assertEquals(expected, decrypted)
+ }
+
+ @Test
+ fun test_encrypt() {
+ val expected = "{\"id\":1554098597199736,\"jsonrpc\":\"2.0\",\"method\":\"wc_sessionUpdate\",\"params\":[{\"approved\":false,\"chainId\":null,\"accounts\":null}]}".hexStringToByteArray()
+ val key = "5caa3a74154cee16bd1b570a1330be46e086474ac2f4720530662ef1a469662c".hexStringToByteArray()
+ val payload = WCCipher.encrypt(data = expected, key = key)
+ val decrypted = WCCipher.decrypt(payload, key)
+ Assert.assertArrayEquals(expected, decrypted)
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/test/java/com/trustwallet/walletconnect/WCSessionStoreTests.kt b/walletconnect/src/test/java/com/trustwallet/walletconnect/WCSessionStoreTests.kt
new file mode 100644
index 0000000..067cfc4
--- /dev/null
+++ b/walletconnect/src/test/java/com/trustwallet/walletconnect/WCSessionStoreTests.kt
@@ -0,0 +1,51 @@
+package com.trustwallet.walletconnect
+
+import android.content.Context
+import android.os.Build
+import com.trustwallet.walletconnect.models.WCPeerMeta
+import com.trustwallet.walletconnect.models.session.WCSession
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [Build.VERSION_CODES.M])
+class WCSessionStoreTests {
+ private val context = RuntimeEnvironment.systemContext
+ private val sharedPreferences = context.getSharedPreferences("tests", Context.MODE_PRIVATE)
+ private val storage = WCSessionStoreType(sharedPreferences)
+
+ companion object {
+ const val SESSION_KEY = "org.walletconnect.session"
+ }
+
+ @Before
+ fun before() {
+ sharedPreferences.edit().clear().commit()
+ }
+
+ @Test
+ fun test_store() {
+ val topic = "topic_1"
+ val session = WCSession.from("wc:$topic@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=some_key")!!
+ val item = WCSessionStoreItem(session, 56, "peerId", "remotePeerId", WCPeerMeta(name = "Some DApp", url = "https://dapp.com"))
+
+ storage.session = item
+ Assert.assertNotNull(sharedPreferences.getString(SESSION_KEY, null))
+ }
+
+ @Test
+ fun test_remove() {
+ val topic = "topic_1"
+ val session = WCSession.from("wc:$topic@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=some_key")!!
+ val item = WCSessionStoreItem(session, 56, "peerId","remotePeerId", WCPeerMeta(name = "Some DApp", url = "https://dapp.com"))
+
+ storage.session = item
+ storage.session = null
+ Assert.assertFalse(sharedPreferences.contains(SESSION_KEY))
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/test/java/com/trustwallet/walletconnect/models/WCSessionTests.kt b/walletconnect/src/test/java/com/trustwallet/walletconnect/models/WCSessionTests.kt
new file mode 100644
index 0000000..0312f76
--- /dev/null
+++ b/walletconnect/src/test/java/com/trustwallet/walletconnect/models/WCSessionTests.kt
@@ -0,0 +1,26 @@
+package com.trustwallet.walletconnect.models
+
+import android.os.Build
+import com.trustwallet.walletconnect.models.session.WCSession
+import org.junit.Test
+
+import org.junit.Assert.*
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [Build.VERSION_CODES.O_MR1])
+class WCSessionTests {
+
+ @Test
+ fun test_from() {
+ val uri = "wc:217374f6-8735-472d-a743-23bd7d26d106@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=d565a3e6cc792fa789bbea26b3f257fb436cfba2de48d2490b3e0248168d4b6b"
+ val session = WCSession.from(uri)
+
+ assertEquals("217374f6-8735-472d-a743-23bd7d26d106", session?.topic)
+ assertEquals("1", session?.version)
+ assertEquals("https://bridge.walletconnect.org", session?.bridge)
+ assertEquals("d565a3e6cc792fa789bbea26b3f257fb436cfba2de48d2490b3e0248168d4b6b", session?.key)
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/test/java/com/trustwallet/walletconnect/models/binance/WCBinanceOrderTests.kt b/walletconnect/src/test/java/com/trustwallet/walletconnect/models/binance/WCBinanceOrderTests.kt
new file mode 100644
index 0000000..4c12755
--- /dev/null
+++ b/walletconnect/src/test/java/com/trustwallet/walletconnect/models/binance/WCBinanceOrderTests.kt
@@ -0,0 +1,178 @@
+package com.trustwallet.walletconnect.models.binance
+
+import com.github.salomonbrys.kotson.fromJson
+import com.github.salomonbrys.kotson.registerTypeAdapter
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonArray
+import com.trustwallet.walletconnect.jsonrpc.JsonRpcRequest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Test
+import java.util.*
+
+class WCBinanceOrderTests {
+ val gson = GsonBuilder()
+ .registerTypeAdapter(cancelOrderDeserializer)
+ .registerTypeAdapter(cancelOrderSerializer)
+ .registerTypeAdapter(tradeOrderDeserializer)
+ .registerTypeAdapter(tradeOrderSerializer)
+ .registerTypeAdapter(transferOrderDeserializer)
+ .registerTypeAdapter(transferOrderSerializer)
+ .create()
+
+ @Test
+ fun test_parseCancelOrder() {
+ val json = """
+ {
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "bnb_sign",
+ "params": [
+ {
+ "account_number": "29",
+ "chain_id": "Binance-Chain-Tigris",
+ "data": null,
+ "memo": "",
+ "msgs": [
+ {
+ "refid": "33BBF307B98146F13D20693CF946C2D77A4CAF28-300",
+ "sender": "bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl",
+ "symbol": "PVT-554_BNB"
+ }
+ ],
+ "sequence": "300",
+ "source": "1"
+ }
+ ]
+ }"""
+
+
+ val request = gson.fromJson>(json)
+ val cancelOrder = gson.fromJson>(request.params).first()
+ assertNotNull(cancelOrder)
+ val cancelOrderJson = gson.toJson(cancelOrder)
+ 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"}]}""")
+ }
+
+ @Test
+ fun test_parseTradeOrder() {
+ val json = """
+ {
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "bnb_sign",
+ "params": [
+ {
+ "account_number": "29",
+ "chain_id": "Binance-Chain-Tigris",
+ "data": null,
+ "memo": "",
+ "msgs": [
+ {
+ "id": "33BBF307B98146F13D20693CF946C2D77A4CAF28-300",
+ "ordertype": 2,
+ "price": 7800,
+ "quantity": 10000000000,
+ "sender": "bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl",
+ "side": 1,
+ "symbol": "PVT-554_BNB",
+ "timeinforce": 1
+ }
+ ],
+ "sequence": "299",
+ "source": "1"
+ }
+ ]
+ }"""
+
+
+ val request = gson.fromJson>(json)
+ val tradeOrder = gson.fromJson>(request.params).first()
+ assertNotNull(tradeOrder)
+ val cancelOrderJson = gson.toJson(tradeOrder)
+ 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}]}""")
+ }
+
+ @Test
+ fun test_parseTransferOrder() {
+ val json = """
+ {
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "bnb_sign",
+ "params": [
+ {
+ "account_number": "29",
+ "chain_id": "Binance-Chain-Tigris",
+ "data": null,
+ "memo": "Testing",
+ "msgs": [
+ {
+ "inputs": [
+ {
+ "address": "bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl",
+ "coins": [
+ {
+ "amount": 1000000,
+ "denom": "BNB"
+ }
+ ]
+ }
+ ],
+ "outputs": [
+ {
+ "address": "bnb14u7newkxwdhcuhddvtg2n8n96m9tqxejsjuuhn",
+ "coins": [
+ {
+ "amount": 1000000,
+ "denom": "BNB"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "sequence": "301",
+ "source": "1"
+ }
+ ]
+ }
+ """
+
+ val request = gson.fromJson>(json)
+ val order = gson.fromJson>(request.params).first()
+ assertNotNull(order)
+ 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"}]}]}]}""")
+ }
+
+ @Test(expected = NoSuchElementException::class)
+ fun test_parseInvalidTradeOrder() {
+ val json = """
+ {
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "bnb_sign",
+ "params": [
+ {
+ "account_number": "29",
+ "chain_id": "Binance-Chain-Tigris",
+ "data": null,
+ "memo": "",
+ "msgs": [
+ {
+ "refid": "33BBF307B98146F13D20693CF946C2D77A4CAF28-300",
+ "sender": "bnb1xwalxpaes9r0z0fqdy70j3kz6aayetegur38gl",
+ "symbol": "PVT-554_BNB"
+ }
+ ],
+ "sequence": "300",
+ "source": "1"
+ }
+ ]
+ }"""
+
+
+ val request = gson.fromJson>(json)
+ gson.fromJson>(request.params).first()
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/test/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradePairTests.kt b/walletconnect/src/test/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradePairTests.kt
new file mode 100644
index 0000000..758b4e8
--- /dev/null
+++ b/walletconnect/src/test/java/com/trustwallet/walletconnect/models/binance/WCBinanceTradePairTests.kt
@@ -0,0 +1,27 @@
+package com.trustwallet.walletconnect.models.binance
+
+import org.junit.Assert.*
+import org.junit.Test
+
+class WCBinanceTradePairTests {
+ @Test
+ fun test_parse() {
+ val symbol = "BNB_ETH.B-261"
+ val pair = WCBinanceTradePair.from(symbol)
+
+ assertEquals(pair?.from, "BNB")
+ assertEquals(pair?.to, "ETH.B")
+
+ val symbol2 = "000-0E1_BNB"
+ val pair2 = WCBinanceTradePair.from(symbol2)
+
+ assertEquals(pair2?.from, "000")
+ assertEquals(pair2?.to, "BNB")
+
+ val symbol3 = "CRYPRICE-150_BTC.B-918"
+ val pair3 = WCBinanceTradePair.from(symbol3)
+
+ assertEquals(pair3?.from, "CRYPRICE")
+ assertEquals(pair3?.to, "BTC.B")
+ }
+}
\ No newline at end of file
diff --git a/walletconnect/src/test/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumTransactionTests.kt b/walletconnect/src/test/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumTransactionTests.kt
new file mode 100644
index 0000000..18b23f1
--- /dev/null
+++ b/walletconnect/src/test/java/com/trustwallet/walletconnect/models/ethereum/WCEthereumTransactionTests.kt
@@ -0,0 +1,58 @@
+package com.trustwallet.walletconnect.models.ethereum
+
+import com.github.salomonbrys.kotson.fromJson
+import com.github.salomonbrys.kotson.registerTypeAdapter
+import com.google.gson.GsonBuilder
+import com.trustwallet.walletconnect.models.binance.ethTransactionSerializer
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+class WCEthereumTransactionTests {
+ private val gson = GsonBuilder()
+ .registerTypeAdapter(ethTransactionSerializer)
+ .create()
+
+ @Test
+ fun test_parseCancelOrder() {
+ val jsonString = """
+ {
+ "from": "0xc36edf48e21cf395b206352a1819de658fd7f988",
+ "gas": "0x77fb",
+ "gasPrice": "0xb2d05e00",
+ "nonce": "0x64",
+ "to": "0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e",
+ "value": "0x0",
+ "data": ""
+ }
+ """
+
+
+ val tx = gson.fromJson(jsonString)
+ assertEquals(tx.gas, "0x77fb")
+ assertNull(tx.gasLimit)
+ }
+
+ @Test
+ fun test_parseMultipleMessages() {
+ val jsonString = """
+ [
+ {
+ "from": "0xc36edf48e21cf395b206352a1819de658fd7f988",
+ "gas": "0x77fb",
+ "gasPrice": "0xb2d05e00",
+ "nonce": "0x64",
+ "to": "0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e",
+ "value": "0x0",
+ "data": ""
+ },
+ ""
+ ]
+ """
+
+ val tx = gson.fromJson>(jsonString)
+ assertEquals(tx.size, 1)
+ assertEquals(tx.first()?.gas, "0x77fb")
+ assertNull(tx.first()?.gasLimit)
+ }
+}
\ No newline at end of file