@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
- private val ethereum by lazy {
10
- Ethereum.getInstance()
11
- }
12
-
13
- override suspend fun connect(): ConnectResult {
14
- val result = ethereum.connect()
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
- override suspend fun signMessage(message: String): String {
42
- val address = ethereum.selectedAddress
43
- ?: throw IllegalStateException("No connected account. Call connect() first.")
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
- // Based on SDK documentation, personal_sign params are: [address, message]
46
- // The SDK handles message encoding internally
47
- val request = EthereumRequest(
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
- val result = ethereum.sendRequest(request)
53
- return when (result) {
54
- is Result.Success.Item -> {
55
- result.value as? String ?: throw IllegalStateException("Invalid signature response")
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
- is Result.Error -> {
58
- throw Exception(result.error.message ?: "Failed to sign message")
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
- else -> {
61
- throw IllegalStateException("Unexpected result type from MetaMask signMessage")
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? = null
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() async throws -> ConnectResult {
8
- // Based on MetaMask iOS SDK docs: let connectResult = await metamaskSDK.connect()
9
- let connectResult = await sdk.connect()
10
-
11
- switch connectResult {
12
- case .success(let value):
13
- // After successful connection, get account info from SDK
14
- guard let account = sdk.account else {
15
- throw NSError(domain: "MetamaskConnector", code: -1, userInfo: [NSLocalizedDescriptionKey: "MetaMask SDK returned no account"])
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) async throws -> String {
24
- // Get the connected account address
25
- guard let account = sdk.account else {
26
- throw NSError(domain: "MetamaskConnector", code: -1, userInfo: [NSLocalizedDescriptionKey: "No connected account. Call connect() first."])
27
- }
28
-
29
- // Based on MetaMask iOS SDK docs, personal_sign params are: [account, message]
30
- // The SDK handles message encoding internally
31
- // Reference: https://github.com/MetaMask/metamask-ios-sdk
32
- let params: [String] = [account.address, message]
33
-
34
- // Create EthereumRequest for personal_sign JSON-RPC method
35
- let request = EthereumRequest(
36
- method: .personalSign,
37
- params: params
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@novastera-oss/nitro-metamask",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "nitro-metamask",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",