@novastera-oss/nitro-metamask 0.6.3 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NitroMetamask.podspec +12 -3
- package/README.md +3 -1
- package/android/build.gradle +14 -32
- package/android/cargo-ecies.gradle +60 -88
- package/android/src/main/aidl/io/metamask/nativesdk/IMessegeService.aidl +8 -0
- package/android/src/main/aidl/io/metamask/nativesdk/IMessegeServiceCallback.aidl +8 -0
- package/android/src/main/java/com/margelo/nitro/nitrometamask/HybridNitroMetamask.kt +101 -3
- package/android/src/main/java/io/metamask/androidsdk/AnyRequest.kt +8 -0
- package/android/src/main/java/io/metamask/androidsdk/ClientMessageServiceCallback.kt +12 -0
- package/android/src/main/java/io/metamask/androidsdk/ClientServiceConnection.kt +42 -0
- package/android/src/main/java/io/metamask/androidsdk/CommunicationClient.kt +525 -0
- package/android/src/main/java/io/metamask/androidsdk/CommunicationClientModule.kt +47 -0
- package/android/src/main/java/io/metamask/androidsdk/CommunicationClientModuleInterface.kt +11 -0
- package/android/src/main/java/io/metamask/androidsdk/Constants.kt +5 -0
- package/android/src/main/java/io/metamask/androidsdk/Crypto.kt +35 -0
- package/android/src/main/java/io/metamask/androidsdk/DappMetadata.kt +36 -0
- package/android/src/main/java/io/metamask/androidsdk/Encryption.kt +9 -0
- package/android/src/main/java/io/metamask/androidsdk/ErrorType.kt +41 -0
- package/android/src/main/java/io/metamask/androidsdk/Ethereum.kt +328 -0
- package/android/src/main/java/io/metamask/androidsdk/EthereumEventCallback.kt +6 -0
- package/android/src/main/java/io/metamask/androidsdk/EthereumMethod.kt +80 -0
- package/android/src/main/java/io/metamask/androidsdk/EthereumRequest.kt +7 -0
- package/android/src/main/java/io/metamask/androidsdk/EthereumState.kt +7 -0
- package/android/src/main/java/io/metamask/androidsdk/KeyExchange.kt +77 -0
- package/android/src/main/java/io/metamask/androidsdk/KeyExchangeMessageType.kt +20 -0
- package/android/src/main/java/io/metamask/androidsdk/KeyStorage.kt +122 -0
- package/android/src/main/java/io/metamask/androidsdk/Logger.kt +18 -0
- package/android/src/main/java/io/metamask/androidsdk/Message.kt +3 -0
- package/android/src/main/java/io/metamask/androidsdk/MessageType.kt +11 -0
- package/android/src/main/java/io/metamask/androidsdk/OriginatorInfo.kt +12 -0
- package/android/src/main/java/io/metamask/androidsdk/RequestError.kt +8 -0
- package/android/src/main/java/io/metamask/androidsdk/RequestInfo.kt +9 -0
- package/android/src/main/java/io/metamask/androidsdk/Result.kt +11 -0
- package/android/src/main/java/io/metamask/androidsdk/RpcRequest.kt +7 -0
- package/android/src/main/java/io/metamask/androidsdk/SDKInfo.kt +6 -0
- package/android/src/main/java/io/metamask/androidsdk/SDKOptions.kt +6 -0
- package/android/src/main/java/io/metamask/androidsdk/SecureStorage.kt +9 -0
- package/android/src/main/java/io/metamask/androidsdk/SessionConfig.kt +10 -0
- package/android/src/main/java/io/metamask/androidsdk/SessionManager.kt +92 -0
- package/android/src/main/java/io/metamask/androidsdk/SubmittedRequest.kt +8 -0
- package/android/src/main/java/io/metamask/androidsdk/TimeStampGenerator.kt +7 -0
- package/android/src/main/jniLibs/arm64-v8a/libecies.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libecies.so +0 -0
- package/android/src/main/jniLibs/x86/libecies.so +0 -0
- package/android/src/main/jniLibs/x86_64/libecies.so +0 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/CancellationStateMachineTest.kt +128 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/ChainIdParsingTest.kt +65 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/ConfigureStateMachineTest.kt +140 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/ConnectSignJsonTest.kt +76 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/MetaMaskInstallationCheckTest.kt +42 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/PersonalSignParamsTest.kt +75 -0
- package/ios/Frameworks/Ecies.xcframework/Info.plist +47 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64/Headers/ecies.h +20 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64/Headers/module.modulemap +4 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64/libecies.a +0 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64-simulator/Headers/ecies.h +20 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64-simulator/Headers/module.modulemap +4 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64-simulator/libecies.a +0 -0
- package/ios/HybridNitroMetamask.swift +119 -54
- package/ios/NitroMetamaskTests/CancellationStateMachineTests.swift +150 -0
- package/ios/NitroMetamaskTests/ChainIdParsingTests.swift +117 -0
- package/ios/NitroMetamaskTests/ConfigureStateMachineTests.swift +174 -0
- package/ios/NitroMetamaskTests/ConnectSignJsonTests.swift +168 -0
- package/ios/NitroMetamaskTests/DefaultDappUrlTests.swift +80 -0
- package/ios/NitroMetamaskTests/PersonalSignParamsTests.swift +101 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/CommClient.swift +43 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/CommClientFactory.swift +17 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/CommLayer.swift +36 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/Deeplink.swift +26 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/DeeplinkClient.swift +199 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/DeeplinkManager.swift +83 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/String.swift +48 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/URLOpener.swift +19 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/SocketClient.swift +27 -0
- package/ios/metamask-ios-sdk/Crypto/Crypto.swift +72 -0
- package/ios/metamask-ios-sdk/Crypto/Encoding.swift +15 -0
- package/ios/metamask-ios-sdk/Crypto/KeyExchange.swift +236 -0
- package/ios/metamask-ios-sdk/DeviceInfo/DeviceInfo.swift +11 -0
- package/ios/metamask-ios-sdk/Ethereum/AppMetadata.swift +28 -0
- package/ios/metamask-ios-sdk/Ethereum/ErrorType.swift +62 -0
- package/ios/metamask-ios-sdk/Ethereum/Ethereum.swift +810 -0
- package/ios/metamask-ios-sdk/Ethereum/EthereumMethod.swift +111 -0
- package/ios/metamask-ios-sdk/Ethereum/EthereumRequest.swift +40 -0
- package/ios/metamask-ios-sdk/Ethereum/EthereumWrapper.swift +10 -0
- package/ios/metamask-ios-sdk/Ethereum/RPCRequest.swift +14 -0
- package/ios/metamask-ios-sdk/Ethereum/RequestError.swift +88 -0
- package/ios/metamask-ios-sdk/Ethereum/ResponseMethod.swift +22 -0
- package/ios/metamask-ios-sdk/Ethereum/SubmitRequest.swift +26 -0
- package/ios/metamask-ios-sdk/Ethereum/TimestampGenerator.swift +16 -0
- package/ios/metamask-ios-sdk/Extensions/NSRecursiveLock.swift +14 -0
- package/ios/metamask-ios-sdk/Extensions/Notification.swift +10 -0
- package/ios/metamask-ios-sdk/Logger/Logging.swift +27 -0
- package/ios/metamask-ios-sdk/Models/AddChainParameters.swift +35 -0
- package/ios/metamask-ios-sdk/Models/Event.swift +19 -0
- package/ios/metamask-ios-sdk/Models/Mappable.swift +40 -0
- package/ios/metamask-ios-sdk/Models/NativeCurrency.swift +25 -0
- package/ios/metamask-ios-sdk/Models/OriginatorInfo.swift +26 -0
- package/ios/metamask-ios-sdk/Models/RequestInfo.swift +18 -0
- package/ios/metamask-ios-sdk/Models/SignContract.swift +48 -0
- package/ios/metamask-ios-sdk/Models/Typealiases.swift +9 -0
- package/ios/metamask-ios-sdk/Persistence/SecureStore.swift +134 -0
- package/ios/metamask-ios-sdk/Persistence/SessionConfig.swift +24 -0
- package/ios/metamask-ios-sdk/Persistence/SessionManager.swift +56 -0
- package/ios/metamask-ios-sdk/SDK/Dependencies.swift +35 -0
- package/ios/metamask-ios-sdk/SDK/MetaMaskSDK.swift +215 -0
- package/ios/metamask-ios-sdk/SDK/SDKInfo.swift +37 -0
- package/ios/metamask-ios-sdk/SDK/SDKOptions.swift +16 -0
- package/lib/commonjs/index.js +50 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +49 -3
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/__tests__/parseNitroError.test.d.ts +2 -0
- package/lib/typescript/src/__tests__/parseNitroError.test.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +43 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/nitro-metamask.nitro.d.ts +29 -1
- package/lib/typescript/src/specs/nitro-metamask.nitro.d.ts.map +1 -1
- package/package.json +21 -12
- package/react-native.config.js +5 -0
- package/rust/ecies-jni/Cargo.lock +50 -86
- package/rust/ecies-jni/Cargo.toml +1 -1
- package/rust/ecies-jni/src/lib.rs +164 -100
- package/src/__tests__/parseNitroError.test.ts +35 -0
- package/src/index.ts +53 -5
- package/src/specs/nitro-metamask.nitro.ts +29 -1
- package/scripts/verify-16k-page-alignment.py +0 -117
- package/scripts/verify-16k-page-alignment.sh +0 -5
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package io.metamask.androidsdk
|
|
2
|
+
|
|
3
|
+
enum class ErrorType(val code: Int) {
|
|
4
|
+
// Ethereum Provider
|
|
5
|
+
USER_REJECTED_REQUEST(4001), // Ethereum Provider User Rejected Request
|
|
6
|
+
UNAUTHORISED_REQUEST(4100), // Ethereum Provider User Rejected Request
|
|
7
|
+
UNSUPPORTED_METHOD(4200), // Ethereum Provider Unsupported Method
|
|
8
|
+
DISCONNECTED(4900), // Ethereum Provider Not Connected
|
|
9
|
+
CHAIN_DISCONNECTED(4901), // Ethereum Provider Chain Not Connected
|
|
10
|
+
UNRECOGNIZED_CHAIN_ID(4902), // Unrecognized chain ID. Try adding the chain using wallet_ADD_ETHEREUM_CHAIN first
|
|
11
|
+
|
|
12
|
+
// Ethereum RPC
|
|
13
|
+
INVALID_INPUT(-32000), // JSON RPC 2.0 Server error
|
|
14
|
+
TRANSACTION_REJECTED(-32003), // Ethereum JSON RPC Transaction Rejected
|
|
15
|
+
INVALID_REQUEST(-32600), // JSON RPC 2.0 Invalid Request
|
|
16
|
+
INVALID_METHOD_PARAMETERS(-32602), // JSON RPC 2.0 Invalid Parameters
|
|
17
|
+
SERVER_ERROR(-32603), // Could be one of many outcomes
|
|
18
|
+
PARSE_ERROR(-32700), // JSON RPC 2.0 Parse error
|
|
19
|
+
UNKNOWN_ERROR(-1); // Check RequestError.code instead
|
|
20
|
+
|
|
21
|
+
companion object {
|
|
22
|
+
fun message(code: Int): String {
|
|
23
|
+
|
|
24
|
+
return when(values().firstOrNull { it.code == code }) {
|
|
25
|
+
USER_REJECTED_REQUEST -> "User rejected request"
|
|
26
|
+
UNAUTHORISED_REQUEST -> "Request not authorised"
|
|
27
|
+
UNSUPPORTED_METHOD -> "Ethereum provider unsupported method"
|
|
28
|
+
DISCONNECTED -> "Ethereum provider not connected"
|
|
29
|
+
CHAIN_DISCONNECTED -> "Ethereum provider chain not connected"
|
|
30
|
+
UNRECOGNIZED_CHAIN_ID -> "Unrecognized chain ID. Try adding the chain using ADD_ETHEREUM_CHAIN first"
|
|
31
|
+
INVALID_INPUT -> "JSON RPC 2.0 Server error"
|
|
32
|
+
TRANSACTION_REJECTED -> "Ethereum transaction rejected"
|
|
33
|
+
INVALID_METHOD_PARAMETERS -> "JSON RPC 2.0 invalid parameters"
|
|
34
|
+
INVALID_REQUEST -> "Invalid request"
|
|
35
|
+
SERVER_ERROR -> "Server error"
|
|
36
|
+
PARSE_ERROR -> "Parse error"
|
|
37
|
+
else -> "The request failed"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
package io.metamask.androidsdk
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import androidx.lifecycle.LiveData
|
|
7
|
+
import androidx.lifecycle.MutableLiveData
|
|
8
|
+
import kotlinx.coroutines.Dispatchers
|
|
9
|
+
import kotlinx.coroutines.runBlocking
|
|
10
|
+
import kotlinx.coroutines.withContext
|
|
11
|
+
import java.lang.ref.WeakReference
|
|
12
|
+
|
|
13
|
+
private const val METAMASK_DEEPLINK = "https://metamask.app.link"
|
|
14
|
+
private const val METAMASK_BIND_DEEPLINK = "$METAMASK_DEEPLINK/bind"
|
|
15
|
+
|
|
16
|
+
class Ethereum(
|
|
17
|
+
private val context: Context,
|
|
18
|
+
private val dappMetadata: DappMetadata,
|
|
19
|
+
sdkOptions: SDKOptions? = null,
|
|
20
|
+
private val logger: Logger = DefaultLogger,
|
|
21
|
+
private val communicationClientModule: CommunicationClientModuleInterface = CommunicationClientModule(context)
|
|
22
|
+
) : EthereumEventCallback {
|
|
23
|
+
|
|
24
|
+
private var connectRequestSent = false
|
|
25
|
+
|
|
26
|
+
val communicationClient: CommunicationClient? by lazy {
|
|
27
|
+
communicationClientModule.provideCommunicationClient(this)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private val storage = communicationClientModule.provideKeyStorage()
|
|
31
|
+
|
|
32
|
+
// Ethereum LiveData
|
|
33
|
+
private val _ethereumState = MutableLiveData(EthereumState("", "", ""))
|
|
34
|
+
private val currentEthereumState: EthereumState
|
|
35
|
+
get() = checkNotNull(ethereumState.value)
|
|
36
|
+
val ethereumState: LiveData<EthereumState> get() = _ethereumState
|
|
37
|
+
|
|
38
|
+
private var cachedChainId = ""
|
|
39
|
+
private var cachedAccount = ""
|
|
40
|
+
|
|
41
|
+
var selectedAddress: String = ethereumState.value?.selectedAddress.takeIf { !it.isNullOrEmpty() } ?: cachedAccount
|
|
42
|
+
var chainId: String = ethereumState.value?.chainId.takeIf { !it.isNullOrEmpty() } ?: cachedChainId
|
|
43
|
+
|
|
44
|
+
var enableDebug: Boolean = true
|
|
45
|
+
set(value) {
|
|
46
|
+
field = value
|
|
47
|
+
communicationClient?.enableDebug = value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
init {
|
|
51
|
+
runBlocking { fetchCachedSession() }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private suspend fun fetchCachedSession() {
|
|
55
|
+
withContext(Dispatchers.IO) {
|
|
56
|
+
try {
|
|
57
|
+
val account = storage.getValue(key = SessionManager.SESSION_ACCOUNT_KEY, file = SessionManager.SESSION_CONFIG_FILE)
|
|
58
|
+
val chainId = storage.getValue(key = SessionManager.SESSION_CHAIN_ID_KEY, file = SessionManager.SESSION_CONFIG_FILE)
|
|
59
|
+
if (account != null && chainId != null) {
|
|
60
|
+
cachedChainId = chainId
|
|
61
|
+
cachedAccount = account
|
|
62
|
+
_ethereumState.postValue(
|
|
63
|
+
currentEthereumState.copy(
|
|
64
|
+
selectedAddress = account,
|
|
65
|
+
chainId = chainId,
|
|
66
|
+
sessionId = communicationClient?.sessionId ?: ""
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
} catch (e: Exception) {
|
|
71
|
+
e.localizedMessage?.let { logger.error(it) }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fun enableDebug(enable: Boolean) = apply {
|
|
77
|
+
this.enableDebug = enable
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private var sessionDuration: Long = SessionManager.DEFAULT_SESSION_DURATION
|
|
81
|
+
|
|
82
|
+
override fun updateAccount(account: String) {
|
|
83
|
+
logger.log("Ethereum:: Selected account changed: $account")
|
|
84
|
+
_ethereumState.postValue(
|
|
85
|
+
currentEthereumState.copy(
|
|
86
|
+
selectedAddress = account,
|
|
87
|
+
sessionId = communicationClient?.sessionId ?: ""
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
if (account.isNotEmpty()) {
|
|
91
|
+
selectedAddress = account
|
|
92
|
+
storage.putValue(account, key = SessionManager.SESSION_ACCOUNT_KEY, SessionManager.SESSION_CONFIG_FILE)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
override fun updateChainId(newChainId: String) {
|
|
97
|
+
logger.log("Ethereum:: ChainId changed: $newChainId")
|
|
98
|
+
_ethereumState.postValue(
|
|
99
|
+
currentEthereumState.copy(
|
|
100
|
+
chainId = newChainId,
|
|
101
|
+
sessionId = communicationClient?.sessionId ?: ""
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
if (newChainId.isNotEmpty()) {
|
|
105
|
+
chainId = newChainId
|
|
106
|
+
storage.putValue(newChainId, key = SessionManager.SESSION_CHAIN_ID_KEY, SessionManager.SESSION_CONFIG_FILE)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fun updateSessionDuration(duration: Long = SessionManager.DEFAULT_SESSION_DURATION) = apply {
|
|
111
|
+
sessionDuration = duration
|
|
112
|
+
communicationClient?.updateSessionDuration(duration)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fun clearSession() {
|
|
116
|
+
disconnect(true)
|
|
117
|
+
storage.clear(SessionManager.SESSION_CONFIG_FILE)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fun connect(callback: ((Result) -> Unit)? = null) {
|
|
121
|
+
connectRequestSent = true
|
|
122
|
+
|
|
123
|
+
val error = dappMetadata.validationError
|
|
124
|
+
if (error != null) {
|
|
125
|
+
callback?.invoke(Result.Error(error))
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
logger.log("Ethereum:: connecting...")
|
|
130
|
+
communicationClient?.dappMetadata = dappMetadata
|
|
131
|
+
communicationClient?.ethereumEventCallbackRef = WeakReference(this)
|
|
132
|
+
communicationClient?.updateSessionDuration(sessionDuration)
|
|
133
|
+
|
|
134
|
+
_ethereumState.postValue(
|
|
135
|
+
currentEthereumState.copy(
|
|
136
|
+
selectedAddress = "",
|
|
137
|
+
chainId = ""
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
requestAccounts(callback)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
fun connectWith(request: EthereumRequest, callback: ((Result) -> Unit)? = null) {
|
|
144
|
+
logger.log("Ethereum:: connecting with ${request.method}...")
|
|
145
|
+
connectRequestSent = true
|
|
146
|
+
communicationClient?.dappMetadata = dappMetadata
|
|
147
|
+
communicationClient?.ethereumEventCallbackRef = WeakReference(this)
|
|
148
|
+
communicationClient?.updateSessionDuration(sessionDuration)
|
|
149
|
+
|
|
150
|
+
_ethereumState.postValue(
|
|
151
|
+
currentEthereumState.copy(
|
|
152
|
+
selectedAddress = "",
|
|
153
|
+
chainId = ""
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
val sendRequest: EthereumRequest = if (request.method == EthereumMethod.METAMASK_CONNECT_WITH.value) {
|
|
157
|
+
request
|
|
158
|
+
} else {
|
|
159
|
+
EthereumRequest(
|
|
160
|
+
method = EthereumMethod.METAMASK_CONNECT_WITH.value,
|
|
161
|
+
params = listOf(request)
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
sendConnectRequest(sendRequest, callback)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fun connectSign(message: String, callback: ((Result) -> Unit)? = null) {
|
|
168
|
+
connectRequestSent = true
|
|
169
|
+
communicationClient?.dappMetadata = dappMetadata
|
|
170
|
+
communicationClient?.ethereumEventCallbackRef = WeakReference(this)
|
|
171
|
+
communicationClient?.updateSessionDuration(sessionDuration)
|
|
172
|
+
|
|
173
|
+
_ethereumState.postValue(
|
|
174
|
+
currentEthereumState.copy(
|
|
175
|
+
selectedAddress = "",
|
|
176
|
+
chainId = ""
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
val connectSignRequest = EthereumRequest(
|
|
180
|
+
method = EthereumMethod.METAMASK_CONNECT_SIGN.value,
|
|
181
|
+
params = listOf(message)
|
|
182
|
+
)
|
|
183
|
+
sendConnectRequest(connectSignRequest, callback)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private fun ethereumRequest(method: EthereumMethod, params: Any?, callback: ((Result) -> Unit)?) {
|
|
187
|
+
sendRequest(EthereumRequest(method = method.value, params = params), callback)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
fun getChainId(callback: ((Result) -> Unit)?) {
|
|
191
|
+
if (connectRequestSent) {
|
|
192
|
+
ethereumRequest(method = EthereumMethod.ETH_CHAIN_ID, params = null, callback)
|
|
193
|
+
} else {
|
|
194
|
+
callback?.invoke(Result.Success.Item(cachedChainId))
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
fun getEthAccounts(callback: ((Result) -> Unit)?) {
|
|
199
|
+
if (connectRequestSent) {
|
|
200
|
+
ethereumRequest(method = EthereumMethod.ETH_ACCOUNTS, params = null, callback)
|
|
201
|
+
} else {
|
|
202
|
+
callback?.invoke(Result.Success.Item(cachedAccount))
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fun getEthBalance(address: String, block: String, callback: ((Result) -> Unit)? = null) {
|
|
207
|
+
ethereumRequest(EthereumMethod.ETH_GET_BALANCE, params = listOf(address, block), callback)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
fun getEthBlockNumber(callback: ((Result) -> Unit)?) {
|
|
211
|
+
ethereumRequest(method = EthereumMethod.ETH_BLOCK_NUMBER, params = null, callback)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
fun getEthEstimateGas(callback: ((Result) -> Unit)?) {
|
|
215
|
+
ethereumRequest(method = EthereumMethod.ETH_ESTIMATE_GAS, params = null, callback)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
fun getWeb3ClientVersion(callback: ((Result) -> Unit)?) {
|
|
219
|
+
ethereumRequest(method = EthereumMethod.WEB3_CLIENT_VERSION, params = listOf<String>(), callback)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fun personalSign(message: String, address: String, callback: ((Result) -> Unit)?) {
|
|
223
|
+
ethereumRequest(method = EthereumMethod.PERSONAL_SIGN, params = listOf(address, message), callback)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
fun ethSignTypedDataV4(typedData: Any, address: String, callback: ((Result) -> Unit)?) {
|
|
227
|
+
ethereumRequest(method = EthereumMethod.ETH_SIGN_TYPED_DATA_V4, params = listOf(address, typedData), callback)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
fun sendTransaction(from: String, to: String, value: String, callback: ((Result) -> Unit)?) {
|
|
231
|
+
ethereumRequest(
|
|
232
|
+
method = EthereumMethod.ETH_SEND_TRANSACTION,
|
|
233
|
+
params = listOf(mutableMapOf("from" to from, "to" to to, "value" to value)),
|
|
234
|
+
callback
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
fun sendRawTransaction(signedTransaction: String, callback: ((Result) -> Unit)?) {
|
|
239
|
+
ethereumRequest(method = EthereumMethod.ETH_SEND_RAW_TRANSACTION, params = listOf(signedTransaction), callback)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fun getBlockTransactionCountByNumber(blockNumber: String, callback: ((Result) -> Unit)?) {
|
|
243
|
+
ethereumRequest(method = EthereumMethod.ETH_GET_BLOCK_TRANSACTION_COUNT_BY_NUMBER, params = listOf(blockNumber), callback)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
fun getBlockTransactionCountByHash(blockHash: String, callback: ((Result) -> Unit)?) {
|
|
247
|
+
ethereumRequest(method = EthereumMethod.ETH_GET_BLOCK_TRANSACTION_COUNT_BY_HASH, params = listOf(blockHash), callback)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
fun getTransactionCount(address: String, tagOrblockNumber: String, callback: ((Result) -> Unit)?) {
|
|
251
|
+
ethereumRequest(method = EthereumMethod.ETH_GET_TRANSACTION_COUNT, params = listOf(address, tagOrblockNumber), callback)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
fun switchEthereumChain(targetChainId: String, callback: ((Result) -> Unit)?) {
|
|
255
|
+
ethereumRequest(method = EthereumMethod.SWITCH_ETHEREUM_CHAIN, params = listOf(mapOf("chainId" to targetChainId)), callback)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private fun sendConnectRequest(request: EthereumRequest, callback: ((Result) -> Unit)?) {
|
|
259
|
+
sendRequest(request, callback)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
fun disconnect(clearSession: Boolean = false) {
|
|
263
|
+
logger.log("Ethereum:: disconnecting...")
|
|
264
|
+
connectRequestSent = false
|
|
265
|
+
communicationClient?.resetState()
|
|
266
|
+
communicationClient?.unbindService()
|
|
267
|
+
|
|
268
|
+
if (clearSession) {
|
|
269
|
+
communicationClient?.clearSession {
|
|
270
|
+
resetEthereumState()
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
resetEthereumState()
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private fun resetEthereumState() {
|
|
278
|
+
_ethereumState.postValue(
|
|
279
|
+
currentEthereumState.copy(
|
|
280
|
+
selectedAddress = "",
|
|
281
|
+
sessionId = communicationClient?.sessionId ?: "",
|
|
282
|
+
chainId = ""
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private fun requestChainId() {
|
|
288
|
+
val chainIdRequest = EthereumRequest(method = EthereumMethod.ETH_CHAIN_ID.value)
|
|
289
|
+
sendRequest(chainIdRequest)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private fun requestAccounts(callback: ((Result) -> Unit)? = null) {
|
|
293
|
+
logger.log("Ethereum:: Requesting ethereum accounts")
|
|
294
|
+
connectRequestSent = true
|
|
295
|
+
val accountsRequest = EthereumRequest(method = EthereumMethod.ETH_REQUEST_ACCOUNTS.value)
|
|
296
|
+
sendConnectRequest(accountsRequest, callback)
|
|
297
|
+
requestChainId()
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
fun sendRequest(request: RpcRequest, callback: ((Result) -> Unit)? = null) {
|
|
301
|
+
logger.log("Ethereum:: Sending request $request")
|
|
302
|
+
|
|
303
|
+
if (!connectRequestSent && selectedAddress.isEmpty()) {
|
|
304
|
+
requestAccounts {
|
|
305
|
+
sendRequest(request, callback)
|
|
306
|
+
}
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
communicationClient?.sendRequest(request) { response ->
|
|
311
|
+
callback?.invoke(response)
|
|
312
|
+
}
|
|
313
|
+
if (EthereumMethod.requiresAuthorisation(request.method)) {
|
|
314
|
+
openMetaMask()
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
fun sendRequestBatch(requests: List<EthereumRequest>, callback: ((Result) -> Unit)? = null) {
|
|
319
|
+
val batchRequest = AnyRequest(method = EthereumMethod.METAMASK_BATCH.value, params = requests)
|
|
320
|
+
sendRequest(batchRequest, callback)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private fun openMetaMask() {
|
|
324
|
+
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(METAMASK_BIND_DEEPLINK))
|
|
325
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
326
|
+
context.startActivity(intent)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
package io.metamask.androidsdk
|
|
2
|
+
|
|
3
|
+
enum class EthereumMethod(val value: String) {
|
|
4
|
+
ETH_SIGN("eth_sign"),
|
|
5
|
+
WEB3_SHA("web3_sha3"),
|
|
6
|
+
ETH_CALL("eth_call"),
|
|
7
|
+
ETH_CHAIN_ID("eth_chainId"),
|
|
8
|
+
ETH_GET_CODE("eth_getCode"),
|
|
9
|
+
ETH_ACCOUNTS("eth_accounts"),
|
|
10
|
+
ETH_GAS_PRICE("eth_gasPrice"),
|
|
11
|
+
PERSONAL_SIGN("personal_sign"),
|
|
12
|
+
ETH_GET_BALANCE("eth_getBalance"),
|
|
13
|
+
WATCH_ASSET("wallet_watchAsset"),
|
|
14
|
+
ETH_BLOCK_NUMBER("eth_blockNumber"),
|
|
15
|
+
ETH_ESTIMATE_GAS("eth_estimateGas"),
|
|
16
|
+
ETH_GET_STORAGE_AT("eth_getStorageAt"),
|
|
17
|
+
ETH_SIGN_TYPED_DATA("eth_signTypedData"),
|
|
18
|
+
ETH_GET_BLOCK_BY_HASH("eth_getBlockByHash"),
|
|
19
|
+
WEB3_CLIENT_VERSION("web3_clientVersion"),
|
|
20
|
+
ETH_REQUEST_ACCOUNTS("eth_requestAccounts"),
|
|
21
|
+
ETH_SIGN_TRANSACTION("eth_signTransaction"),
|
|
22
|
+
ETH_SEND_TRANSACTION("eth_sendTransaction"),
|
|
23
|
+
ETH_SIGN_TYPED_DATA_V3("eth_signTypedData_v3"),
|
|
24
|
+
ETH_SIGN_TYPED_DATA_V4("eth_signTypedData_v4"),
|
|
25
|
+
ADD_ETHEREUM_CHAIN("wallet_addEthereumChain"),
|
|
26
|
+
METAMASK_BATCH("metamask_batch"),
|
|
27
|
+
METAMASK_OPEN("metamask_open"),
|
|
28
|
+
PERSONAL_EC_RECOVER("personal_ecRecover"),
|
|
29
|
+
WALLET_REVOKE_PERMISSIONS("wallet_revokePermissions"),
|
|
30
|
+
WALLET_REQUEST_PERMISSIONS("wallet_requestPermissions"),
|
|
31
|
+
WALLET_GET_PERMISSIONS("wallet_getPermissions"),
|
|
32
|
+
METAMASK_CONNECT_WITH("metamask_connectwith"),
|
|
33
|
+
METAMASK_CONNECT_SIGN("metamask_connectSign"),
|
|
34
|
+
METAMASK_CHAIN_CHANGED("metamask_chainChanged"),
|
|
35
|
+
ETH_SEND_RAW_TRANSACTION("eth_sendRawTransaction"),
|
|
36
|
+
SWITCH_ETHEREUM_CHAIN("wallet_switchEthereumChain"),
|
|
37
|
+
ETH_GET_TRANSACTION_COUNT("eth_getTransactionCount"),
|
|
38
|
+
METAMASK_ACCOUNTS_CHANGED("metamask_accountsChanged"),
|
|
39
|
+
ETH_GET_TRANSACTION_BY_HASH("eth_getTransactionByHash"),
|
|
40
|
+
ETH_GET_TRANSACTION_RECEIPT("eth_getTransactionReceipt"),
|
|
41
|
+
GET_METAMASK_PROVIDER_STATE("metamask_getProviderState"),
|
|
42
|
+
ETH_GET_BLOCK_TRANSACTION_COUNT_BY_HASH("eth_getBlockTransactionCountByHash"),
|
|
43
|
+
ETH_GET_BLOCK_TRANSACTION_COUNT_BY_NUMBER("eth_getBlockTransactionCountByNumber"),
|
|
44
|
+
UNKNOWN("unknown");
|
|
45
|
+
|
|
46
|
+
companion object {
|
|
47
|
+
fun hasMethod(method: String): Boolean {
|
|
48
|
+
return enumValues<EthereumMethod>()
|
|
49
|
+
.toList()
|
|
50
|
+
.map { it.value }
|
|
51
|
+
.contains(method)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fun requiresAuthorisation(method: String): Boolean {
|
|
55
|
+
val authorisationMethods: List<String> = listOf(
|
|
56
|
+
ETH_SIGN, WATCH_ASSET, PERSONAL_SIGN, METAMASK_BATCH, WALLET_GET_PERMISSIONS, WALLET_REVOKE_PERMISSIONS,
|
|
57
|
+
ADD_ETHEREUM_CHAIN, SWITCH_ETHEREUM_CHAIN, METAMASK_CONNECT_WITH, WALLET_REQUEST_PERMISSIONS,
|
|
58
|
+
ETH_SEND_TRANSACTION, ETH_SIGN_TRANSACTION, ETH_REQUEST_ACCOUNTS, METAMASK_CONNECT_SIGN, PERSONAL_EC_RECOVER,
|
|
59
|
+
ETH_SIGN_TYPED_DATA, ETH_SIGN_TYPED_DATA_V3, ETH_SIGN_TYPED_DATA_V4, ETH_ACCOUNTS, METAMASK_OPEN
|
|
60
|
+
).map { it.value }
|
|
61
|
+
|
|
62
|
+
return authorisationMethods.contains(method)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fun isReadOnly(method: String): Boolean {
|
|
66
|
+
return !requiresAuthorisation(method)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fun isResultMethod(method: String): Boolean {
|
|
70
|
+
val resultMethods: List<String> = listOf(
|
|
71
|
+
ETH_SIGN, ETH_CHAIN_ID, PERSONAL_SIGN, METAMASK_CONNECT_WITH, WALLET_GET_PERMISSIONS,
|
|
72
|
+
ADD_ETHEREUM_CHAIN, SWITCH_ETHEREUM_CHAIN, METAMASK_BATCH, WALLET_REQUEST_PERMISSIONS,
|
|
73
|
+
ETH_SIGN_TRANSACTION, ETH_SEND_TRANSACTION, METAMASK_CONNECT_SIGN, WALLET_REVOKE_PERMISSIONS,
|
|
74
|
+
WATCH_ASSET, ETH_REQUEST_ACCOUNTS, GET_METAMASK_PROVIDER_STATE,ETH_ACCOUNTS,
|
|
75
|
+
ETH_SIGN_TYPED_DATA, ETH_SIGN_TYPED_DATA_V3, ETH_SIGN_TYPED_DATA_V4,
|
|
76
|
+
).map { it.value }
|
|
77
|
+
return resultMethods.contains(method)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
package io.metamask.androidsdk
|
|
2
|
+
|
|
3
|
+
import io.metamask.androidsdk.KeyExchangeMessageType.*
|
|
4
|
+
|
|
5
|
+
data class KeyExchangeMessage(
|
|
6
|
+
val type: String,
|
|
7
|
+
val publicKey: String?
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
class KeyExchange(private val crypto: Encryption = Crypto(), private val logger: Logger = DefaultLogger) {
|
|
11
|
+
companion object {
|
|
12
|
+
const val TYPE = "type"
|
|
13
|
+
const val PUBLIC_KEY = "public_key"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private var privateKey: String? = null
|
|
17
|
+
var publicKey: String? = null
|
|
18
|
+
private var theirPublicKey: String? = null
|
|
19
|
+
private var isKeysExchanged = false
|
|
20
|
+
|
|
21
|
+
init {
|
|
22
|
+
reset()
|
|
23
|
+
crypto.onInitialized = {
|
|
24
|
+
reset()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private fun setIsKeysExchanged(newValue: Boolean) {
|
|
29
|
+
synchronized(this) {
|
|
30
|
+
isKeysExchanged = newValue
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fun keysExchanged(): Boolean {
|
|
35
|
+
synchronized(this) {
|
|
36
|
+
return isKeysExchanged
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fun reset() {
|
|
41
|
+
privateKey = crypto.generatePrivateKey()
|
|
42
|
+
privateKey?.let {
|
|
43
|
+
publicKey = crypto.publicKey(it)
|
|
44
|
+
}
|
|
45
|
+
setIsKeysExchanged(false)
|
|
46
|
+
theirPublicKey = null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fun encrypt(message: String): String {
|
|
50
|
+
val key: String = theirPublicKey ?: throw NullPointerException("theirPublicKey is null")
|
|
51
|
+
return crypto.encrypt(key, message)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fun decrypt(message: String): String {
|
|
55
|
+
val key: String = privateKey ?: throw NullPointerException("privateKey is null")
|
|
56
|
+
return crypto.decrypt(key, message)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fun complete() {
|
|
60
|
+
logger.log("KeyExchange:: Key exchange complete")
|
|
61
|
+
setIsKeysExchanged(true)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fun nextKeyExchangeMessage(current: KeyExchangeMessage): KeyExchangeMessage? {
|
|
65
|
+
current.publicKey?.let {
|
|
66
|
+
theirPublicKey = it
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return when(current.type) {
|
|
70
|
+
KEY_HANDSHAKE_START.name -> KeyExchangeMessage(KEY_HANDSHAKE_SYN.name, publicKey)
|
|
71
|
+
KEY_HANDSHAKE_SYN.name -> KeyExchangeMessage(KEY_HANDSHAKE_SYNACK.name, publicKey)
|
|
72
|
+
KEY_HANDSHAKE_SYNACK.name -> KeyExchangeMessage(KEY_HANDSHAKE_ACK.name, publicKey)
|
|
73
|
+
KEY_HANDSHAKE_ACK.name -> null
|
|
74
|
+
else -> null
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package io.metamask.androidsdk
|
|
2
|
+
|
|
3
|
+
import kotlinx.serialization.SerialName
|
|
4
|
+
import kotlinx.serialization.Serializable
|
|
5
|
+
|
|
6
|
+
@Serializable
|
|
7
|
+
enum class KeyExchangeMessageType {
|
|
8
|
+
@SerialName("none")
|
|
9
|
+
NONE,
|
|
10
|
+
@SerialName("key_handshake_start")
|
|
11
|
+
KEY_HANDSHAKE_START,
|
|
12
|
+
@SerialName("key_handshake_check")
|
|
13
|
+
KEY_HANDSHAKE_CHECK,
|
|
14
|
+
@SerialName("key_exchange_SYN")
|
|
15
|
+
KEY_HANDSHAKE_SYN,
|
|
16
|
+
@SerialName("key_exchange_SYNACK")
|
|
17
|
+
KEY_HANDSHAKE_SYNACK,
|
|
18
|
+
@SerialName("key_exchange_ACK")
|
|
19
|
+
KEY_HANDSHAKE_ACK
|
|
20
|
+
}
|