Sending transaction using MetaMaskSDK.request() in native iOS (SwiftUI)

I’m developing a native (SwiftUI) iOS mobile app for my dissertation project and I’m trying to send a transaction to my contract using the eth_sendTransaction in this example: github. com/MetaMask/MetaMask-ios-sdk?tab=readme-ov-file#example-send-transaction . However, when I’m sending the transaction. It will open the MetaMask wallet and immediately go back to my app without any confirmation on the MetaMask.

I’m not sure why the deeplink detects an account change, which never occured.

Function to connect to MetaMask

    func connectAndSign() async {
        showProgressView = true
        
        guard let url = URL(string: "metamask://"), UIApplication.shared.canOpenURL(url) else {
            print("MetaMask is not installed.")
            errorMessage = "MetaMask is not installed. Please install MetaMask."
            return
        }
        
        metaMaskSDK.clearSession()
        do {
            let jsonString = try await withTimeout(seconds: 30) {
                try await withCheckedThrowingContinuation { continuation in
                    DeepLinkManager.shared.setContinuation(continuation)
                    
                    Task {
                        _ = await self.metaMaskSDK.connectAndSign(message: "I hereby request to register as a patient.")
                    }
                }
            }
            
            // Parse the successful response
            guard let jsonData = jsonString.data(using: .utf8),
                  let response = try? JSONSerialization.jsonObject(with: jsonData) as? [String: String] else {
                throw ConnectionError.invalidResponseFormat
            }
            
            // Update UI and SDK state
            DispatchQueue.main.async {

                self.result = """
                Connected Account: \(response["account"] ?? "")
                Signature: \(response["signature"] ?? "")
                Chain ID: \(response["chainId"] ?? "")
                """
                self.metaMaskSDK.account = response["account"] ?? ""
                
                self.isConnected = true
                self.account = self.metaMaskSDK.account
                self.chainId = self.metaMaskSDK.chainId
                self.webSocketManager = WebSocketManager(userAddress: self.metaMaskSDK.account)
                 
                self.dismiss()
            }
            
        } catch {
            DispatchQueue.main.async {
                self.errorMessage = error.localizedDescription
            }
        }
        
        showProgressView = false
    }

Function to send data

func sendHealthDataTransaction(heartRate: Int, oxygenLevel: Int, contractAddress: String) async throws -> String {
        // 1. Verify account consistency
        let currentAccount = metaMaskSDK.account
        guard !currentAccount.isEmpty else {
            throw NSError(domain: "MetaMask", code: -1,
                         userInfo: [NSLocalizedDescriptionKey: "No connected account"])
        }
        
        // 2. Store the account we're using for this transaction
        let transactionAccount = currentAccount
        
        print("My account: \(metaMaskSDK.account)")
        
        // 3. Prepare transaction
        let txData = try await prepareTransaction(
            userAddress: transactionAccount,
            heartRate: heartRate,
            oxygenLevel: oxygenLevel
        )
        
        // 4. Create and send transaction
        let transaction = HealthDataTransaction(
            to: contractAddress,
            from: transactionAccount,
            value: "0x0",
            data: txData.data,
            gas: txData.gas,
            gasPrice: txData.gasPrice
        )
        
        let request = EthereumRequest(
            method: .ethSendTransaction,
            params: [transaction]
        )
        
        // 5. Send with account verification
        return try await withCheckedThrowingContinuation { continuation in
            DeepLinkManager.shared.setContinuation(continuation)
            
            Task {
                do {
                    print("Starting transaction with account:", transactionAccount)
                    print("Current account after return:", metaMaskSDK.account)
                    
                    // Verify account hasn't changed
                    if self.metaMaskSDK.account != transactionAccount {
                                        throw ConnectionError.accountChanged
                                    }
                    
                    let result = try await self.metaMaskSDK.request(request)
                    
                    // Final verification
                    if self.metaMaskSDK.account != transactionAccount {
                        print("Account changed during transaction")
                        throw NSError(domain: "MetaMask", code: -32602,
                                    userInfo: [NSLocalizedDescriptionKey: "Account changed during transaction"])
                    }
                     
                } catch {
                    continuation.resume(throwing: error)
                }
            }
        }
    }

These are the logs:

This is the response from my API to prepare the transaction:
API Response: [“nonce”: 0x5, “to”: 0xd8e8DD50888C2159ce0c200bA3047149B3911b8A, “data”: 0xd8db9b7800000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000062, “gas”: 0x30d40]

Deeplink:
My account: 0xe361EcbfEB52E224Cfe5f8c00868934aDf1bFDD7
API Response: [“nonce”: 0x5, “to”: 0xd8e8DD50888C2159ce0c200bA3047149B3911b8A, “data”: 0xd8db9b7800000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000062, “gas”: 0x30d40]
Starting transaction with account: 0xe361EcbfEB52E224Cfe5f8c00868934aDf1bFDD7
Current account after return: 0xe361EcbfEB52E224Cfe5f8c00868934aDf1bFDD7
App moving to background (MetaMask opening)
[DEEP LINK] Received URL: myhealth://mmsdk?message=eyJkYXRhIjp7ImlkIjoiMTc0MzQzMzQ4ODY0NiIsImVycm9yIjp7ImNvZGUiOi0zMjYwMiwibWVzc2FnZSI6IlRoZSBzZWxlY3RlZCBhY2NvdW50IGhhcyBjaGFuZ2VkLiBQbGVhc2UgdHJ5IGFnYWluLiJ9LCJqc29ucnBjIjoiMi4wIiwiY2hhaW5JZCI6IjB4NjEiLCJhY2NvdW50cyI6WyIweGUzNjFFY2JmRUI1MkUyMjRDZmU1ZjhjMDA4Njg5MzRhRGYxYkZERDciXX0sIm5hbWUiOiJtZXRhbWFzay1wcm92aWRlciJ9
:red_circle: Account changed error detected
[DEEP LINK] Received URL: myhealth://mmsdk?message=eyJkYXRhIjp7ImlkIjoiMTc0MzQzMzQ4ODY0NiIsImVycm9yIjp7ImNvZGUiOi0zMjYwMiwibWVzc2FnZSI6IlRoZSBzZWxlY3RlZCBhY2NvdW50IGhhcyBjaGFuZ2VkLiBQbGVhc2UgdHJ5IGFnYWluLiJ9LCJqc29ucnBjIjoiMi4wIiwiY2hhaW5JZCI6IjB4NjEiLCJhY2NvdW50cyI6WyIweGUzNjFFY2JmRUI1MkUyMjRDZmU1ZjhjMDA4Njg5MzRhRGYxYkZERDciXX0sIm5hbWUiOiJtZXRhbWFzay1wcm92aWRlciJ9
:warning: No active continuation to handle deep link
Account changed - please reconnect
App returned to foreground

1 Like

This could be related to how deep linking handles wallet interactions in iOS applications. When you initiate a transaction using MetaMask’s SDK, there’s a specific flow that should occur between your app and MetaMask. Currently, this flow is being interrupted prematurely, causing the immediate return without showing the confirmation dialog.

Please see the MetaMask SDK documentation here: Welcome | MetaMask developer documentation

Also, please share your problem in the MetaMask SDK GitHub Discussions: MetaMask/metamask-sdk · Discussions · GitHub