@novastera-oss/nitro-metamask 0.2.2 → 0.2.3
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.
|
@@ -1,64 +1,92 @@
|
|
|
1
1
|
package com.margelo.nitro.nitrometamask
|
|
2
2
|
|
|
3
|
+
import com.margelo.nitro.Promise
|
|
3
4
|
import io.metamask.androidsdk.Ethereum
|
|
4
|
-
import io.metamask.androidsdk.EthereumRequest
|
|
5
|
-
import io.metamask.androidsdk.EthereumMethod
|
|
6
5
|
import io.metamask.androidsdk.Result
|
|
6
|
+
import io.metamask.androidsdk.DappMetadata
|
|
7
|
+
import io.metamask.androidsdk.SDKOptions
|
|
8
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
9
|
+
import kotlinx.coroutines.suspendCoroutine
|
|
7
10
|
|
|
8
11
|
class HybridMetamaskConnector : HybridMetamaskConnectorSpec() {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return when (result) {
|
|
16
|
-
is Result.Success.Item -> {
|
|
17
|
-
// result.value contains the connection result
|
|
18
|
-
// Based on SDK docs, this should contain address and chainId
|
|
19
|
-
val connectionResult = result.value
|
|
20
|
-
// Extract address and chainId from the result
|
|
21
|
-
// The SDK returns account info in result.value
|
|
22
|
-
val address = ethereum.selectedAddress
|
|
23
|
-
?: throw IllegalStateException("MetaMask SDK returned no address")
|
|
24
|
-
val chainId = ethereum.chainId
|
|
25
|
-
?: throw IllegalStateException("MetaMask SDK returned no chainId")
|
|
26
|
-
|
|
27
|
-
ConnectResult(
|
|
28
|
-
address = address,
|
|
29
|
-
chainId = chainId.toString()
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
is Result.Error -> {
|
|
33
|
-
throw Exception(result.error.message ?: "Failed to connect to MetaMask")
|
|
34
|
-
}
|
|
35
|
-
else -> {
|
|
36
|
-
throw IllegalStateException("Unexpected result type from MetaMask connect")
|
|
37
|
-
}
|
|
12
|
+
companion object {
|
|
13
|
+
@Volatile
|
|
14
|
+
private var reactContext: ReactApplicationContext? = null
|
|
15
|
+
|
|
16
|
+
fun setReactContext(context: ReactApplicationContext) {
|
|
17
|
+
reactContext = context
|
|
38
18
|
}
|
|
39
19
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
20
|
+
|
|
21
|
+
// Initialize Ethereum SDK with Context, DappMetadata, and SDKOptions
|
|
22
|
+
// Based on: https://github.com/MetaMask/metamask-android-sdk
|
|
23
|
+
// The SDK requires a Context for initialization
|
|
24
|
+
private val ethereum: Ethereum by lazy {
|
|
25
|
+
val context = reactContext?.applicationContext
|
|
26
|
+
?: throw IllegalStateException("ReactApplicationContext not initialized. Make sure NitroMetamaskPackage is properly registered.")
|
|
44
27
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
method = EthereumMethod.PERSONAL_SIGN.value,
|
|
49
|
-
params = listOf(address, message)
|
|
28
|
+
val dappMetadata = DappMetadata(
|
|
29
|
+
name = "Nitro MetaMask Connector",
|
|
30
|
+
url = "https://novastera.com"
|
|
50
31
|
)
|
|
32
|
+
val sdkOptions = SDKOptions()
|
|
51
33
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
34
|
+
Ethereum(context, dappMetadata, sdkOptions)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
override fun connect(): Promise<ConnectResult> {
|
|
38
|
+
// Use Promise.async with coroutines for best practice in Nitro modules
|
|
39
|
+
// Reference: https://nitro.margelo.com/docs/types/promises
|
|
40
|
+
return Promise.async {
|
|
41
|
+
// Convert callback-based connect() to suspend function using suspendCoroutine
|
|
42
|
+
val result = suspendCoroutine<Result> { continuation ->
|
|
43
|
+
ethereum.connect { callbackResult ->
|
|
44
|
+
continuation.resume(callbackResult)
|
|
45
|
+
}
|
|
56
46
|
}
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
|
|
48
|
+
when (result) {
|
|
49
|
+
is Result.Success.Item -> {
|
|
50
|
+
// After successful connection, get account info from SDK
|
|
51
|
+
val address = ethereum.selectedAddress
|
|
52
|
+
?: throw IllegalStateException("MetaMask SDK returned no address after connection")
|
|
53
|
+
val chainId = ethereum.chainId
|
|
54
|
+
?: throw IllegalStateException("MetaMask SDK returned no chainId after connection")
|
|
55
|
+
|
|
56
|
+
ConnectResult(
|
|
57
|
+
address = address,
|
|
58
|
+
chainId = chainId.toString()
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
is Result.Error -> {
|
|
62
|
+
throw Exception(result.error.message ?: "Failed to connect to MetaMask")
|
|
63
|
+
}
|
|
64
|
+
else -> {
|
|
65
|
+
throw IllegalStateException("Unexpected result type from MetaMask connect")
|
|
66
|
+
}
|
|
59
67
|
}
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
override fun signMessage(message: String): Promise<String> {
|
|
72
|
+
// Use Promise.async with coroutines for best practice in Nitro modules
|
|
73
|
+
// Reference: https://nitro.margelo.com/docs/types/promises
|
|
74
|
+
return Promise.async {
|
|
75
|
+
// Use the convenience method connectSign() which connects and signs in one call
|
|
76
|
+
// Based on SDK docs: ethereum.connectSign(message) returns Result synchronously
|
|
77
|
+
// Reference: https://github.com/MetaMask/metamask-android-sdk
|
|
78
|
+
when (val result = ethereum.connectSign(message)) {
|
|
79
|
+
is Result.Success.Item -> {
|
|
80
|
+
// Extract signature from result
|
|
81
|
+
result.value as? String
|
|
82
|
+
?: throw IllegalStateException("Invalid signature response format")
|
|
83
|
+
}
|
|
84
|
+
is Result.Error -> {
|
|
85
|
+
throw Exception(result.error.message ?: "Failed to sign message")
|
|
86
|
+
}
|
|
87
|
+
else -> {
|
|
88
|
+
throw IllegalStateException("Unexpected result type from MetaMask signMessage")
|
|
89
|
+
}
|
|
62
90
|
}
|
|
63
91
|
}
|
|
64
92
|
}
|
|
@@ -6,7 +6,11 @@ import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
|
6
6
|
import com.facebook.react.BaseReactPackage
|
|
7
7
|
|
|
8
8
|
class NitroMetamaskPackage : BaseReactPackage() {
|
|
9
|
-
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule?
|
|
9
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
10
|
+
// Store the ReactApplicationContext for use in HybridMetamaskConnector
|
|
11
|
+
HybridMetamaskConnector.setReactContext(reactContext)
|
|
12
|
+
return null
|
|
13
|
+
}
|
|
10
14
|
|
|
11
15
|
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider { HashMap() }
|
|
12
16
|
|
|
@@ -1,53 +1,52 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import MetaMaskSDK
|
|
3
|
+
import NitroModules
|
|
3
4
|
|
|
4
5
|
class HybridMetamaskConnector: HybridMetamaskConnectorSpec {
|
|
5
6
|
private let sdk = MetaMaskSDK.shared
|
|
6
7
|
|
|
7
|
-
func connect()
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
func connect() -> Promise<ConnectResult> {
|
|
9
|
+
// Use Promise.async with Swift async/await for best practice in Nitro modules
|
|
10
|
+
// Reference: https://nitro.margelo.com/docs/types/promises
|
|
11
|
+
return Promise.async {
|
|
12
|
+
// Based on MetaMask iOS SDK docs: let connectResult = await metamaskSDK.connect()
|
|
13
|
+
// Reference: https://github.com/MetaMask/metamask-ios-sdk
|
|
14
|
+
let connectResult = try await self.sdk.connect()
|
|
15
|
+
|
|
16
|
+
switch connectResult {
|
|
17
|
+
case .success(let value):
|
|
18
|
+
// After successful connection, get account info from SDK
|
|
19
|
+
// Note: sdk.account is a String (address), not an object
|
|
20
|
+
// Reference: https://raw.githubusercontent.com/MetaMask/metamask-ios-sdk/924d91bb3e98a5383c3082d6d5ba3ddac9e1c565/README.md
|
|
21
|
+
guard let address = self.sdk.account, !address.isEmpty else {
|
|
22
|
+
throw NSError(domain: "MetamaskConnector", code: -1, userInfo: [NSLocalizedDescriptionKey: "MetaMask SDK returned no address after connection"])
|
|
23
|
+
}
|
|
24
|
+
guard let chainId = self.sdk.chainId, !chainId.isEmpty else {
|
|
25
|
+
throw NSError(domain: "MetamaskConnector", code: -1, userInfo: [NSLocalizedDescriptionKey: "MetaMask SDK returned no chainId after connection"])
|
|
26
|
+
}
|
|
27
|
+
return ConnectResult(address: address, chainId: chainId)
|
|
28
|
+
case .failure(let error):
|
|
29
|
+
throw error
|
|
16
30
|
}
|
|
17
|
-
return ConnectResult(address: account.address, chainId: "\(account.chainId)")
|
|
18
|
-
case .failure(let error):
|
|
19
|
-
throw error
|
|
20
31
|
}
|
|
21
32
|
}
|
|
22
33
|
|
|
23
|
-
func signMessage(message: String)
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// Make the request using the SDK's async request method
|
|
41
|
-
let result = try await sdk.request(request)
|
|
42
|
-
|
|
43
|
-
// Extract signature from response
|
|
44
|
-
// The signature should be a hex-encoded string (0x-prefixed)
|
|
45
|
-
if let signature = result as? String {
|
|
46
|
-
return signature
|
|
47
|
-
} else if let dict = result as? [String: Any], let sig = dict["signature"] as? String ?? dict["result"] as? String {
|
|
48
|
-
return sig
|
|
49
|
-
} else {
|
|
50
|
-
throw NSError(domain: "MetamaskConnector", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid signature response format"])
|
|
34
|
+
func signMessage(message: String) -> Promise<String> {
|
|
35
|
+
// Use Promise.async with Swift async/await for best practice in Nitro modules
|
|
36
|
+
// Reference: https://nitro.margelo.com/docs/types/promises
|
|
37
|
+
return Promise.async {
|
|
38
|
+
// Use the convenience method connectAndSign() which connects and signs in one call
|
|
39
|
+
// This is equivalent to Android's connectSign() method
|
|
40
|
+
// Reference: https://raw.githubusercontent.com/MetaMask/metamask-ios-sdk/924d91bb3e98a5383c3082d6d5ba3ddac9e1c565/README.md
|
|
41
|
+
// Example: https://raw.githubusercontent.com/MetaMask/metamask-ios-sdk/924d91bb3e98a5383c3082d6d5ba3ddac9e1c565/Example/metamask-ios-sdk/SignView.swift
|
|
42
|
+
let connectSignResult = try await self.sdk.connectAndSign(message: message)
|
|
43
|
+
|
|
44
|
+
switch connectSignResult {
|
|
45
|
+
case .success(let signature):
|
|
46
|
+
return signature
|
|
47
|
+
case .failure(let error):
|
|
48
|
+
throw error
|
|
49
|
+
}
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
52
|
}
|