Skip to main content

Overview

Message signing allows users to prove ownership of their wallet by signing arbitrary messages. This is commonly used for authentication and verification.

Prerequisites

Sign a Message

import DynamicSDKSwift

let sdk = DynamicSDK.instance()

func signMessage(wallet: BaseWallet, message: String) async {
    do {
        let signature = try await sdk.wallets.signMessage(
            wallet: wallet,
            message: message
        )
        print("Message signed successfully!")
        print("Signature: \(signature)")
    } catch {
        print("Failed to sign message: \(error)")
    }
}

// Usage
let wallet = sdk.wallets.userWallets.first!
await signMessage(wallet: wallet, message: "Hello, Dynamic!")

Sign Message with SwiftUI

import SwiftUI
import DynamicSDKSwift

struct SignMessageView: View {
    let wallet: BaseWallet

    @State private var message = ""
    @State private var signature: String?
    @State private var isLoading = false
    @State private var errorMessage: String?

    private let sdk = DynamicSDK.instance()

    var body: some View {
        VStack(spacing: 16) {
            TextField("Enter message to sign", text: $message)
                .textFieldStyle(.roundedBorder)

            Button("Sign Message") {
                signMessage()
            }
            .disabled(message.isEmpty || isLoading)

            if isLoading {
                ProgressView()
            }

            if let signature = signature {
                VStack(alignment: .leading, spacing: 8) {
                    Text("Signature:")
                        .font(.caption)
                        .foregroundColor(.secondary)

                    Text(signature)
                        .font(.system(.caption, design: .monospaced))
                        .lineLimit(4)
                        .truncationMode(.middle)

                    Button("Copy") {
                        UIPasteboard.general.string = signature
                    }
                    .font(.caption)
                }
                .padding()
                .background(Color(.systemGray6))
                .cornerRadius(8)
            }

            if let error = errorMessage {
                Text(error)
                    .foregroundColor(.red)
                    .font(.caption)
            }
        }
        .padding()
    }

    private func signMessage() {
        isLoading = true
        errorMessage = nil
        signature = nil

        Task { @MainActor in
            do {
                signature = try await sdk.wallets.signMessage(
                    wallet: wallet,
                    message: message
                )
            } catch {
                errorMessage = "Failed to sign: \(error.localizedDescription)"
            }
            isLoading = false
        }
    }
}

Common Use Cases

Authentication

/// Sign a message to prove wallet ownership
func authenticateWithSignature(wallet: BaseWallet) async throws -> String {
    let nonce = UUID().uuidString
    let message = "Sign this message to authenticate: \(nonce)"

    let signature = try await sdk.wallets.signMessage(
        wallet: wallet,
        message: message
    )

    // Send signature to your backend for verification
    return signature
}

Signing User Actions

/// Sign a message to confirm user action
func signUserAction(
    wallet: BaseWallet,
    action: String,
    timestamp: Date
) async throws -> String {
    let message = """
    Action: \(action)
    Wallet: \(wallet.address)
    Timestamp: \(timestamp.timeIntervalSince1970)
    """

    return try await sdk.wallets.signMessage(
        wallet: wallet,
        message: message
    )
}

Off-Chain Signatures

/// Create off-chain signature for gasless transactions
func signOffChainPermit(
    wallet: BaseWallet,
    spender: String,
    amount: String,
    deadline: Int
) async throws -> String {
    let message = """
    Permit:
    Spender: \(spender)
    Amount: \(amount)
    Deadline: \(deadline)
    """

    return try await sdk.wallets.signMessage(
        wallet: wallet,
        message: message
    )
}

Verify Signatures

While signature verification typically happens on the backend or smart contract, here’s how to structure the verification data:
struct SignatureData {
    let message: String
    let signature: String
    let signerAddress: String

    func toJSON() -> [String: String] {
        return [
            "message": message,
            "signature": signature,
            "signer": signerAddress
        ]
    }
}

// Usage
let signatureData = SignatureData(
    message: "Hello, Dynamic!",
    signature: signature,
    signerAddress: wallet.address
)

// Send to backend for verification
let jsonData = signatureData.toJSON()

Best Practices

1. Always Handle Errors

do {
    let signature = try await sdk.wallets.signMessage(wallet: wallet, message: message)
    // Use signature
} catch {
    // Show user-friendly error message
    print("Could not sign message. Please try again.")
}

2. Include Context in Messages

// Bad: Unclear message
let message = "12345"

// Good: Clear message with context
let message = """
Welcome to MyApp!

Click "Sign" to prove you own this wallet.

Wallet: \(wallet.address)
Nonce: 12345
"""

3. Show Loading States

@Published var isLoading = false

func signMessage(wallet: BaseWallet, message: String) {
    isLoading = true
    Task {
        defer { isLoading = false }
        let signature = try await sdk.wallets.signMessage(wallet: wallet, message: message)
        // Process signature
    }
}

4. Clear Sensitive Data

var signature: String? = try await sdk.wallets.signMessage(wallet: wallet, message: message)
// ... use signature ...
signature = nil  // Clear from memory

Error Handling

do {
    let signature = try await sdk.wallets.signMessage(
        wallet: wallet,
        message: message
    )
} catch {
    let errorDesc = error.localizedDescription.lowercased()

    if errorDesc.contains("rejected") || errorDesc.contains("denied") {
        print("User rejected the signature request")
    } else if errorDesc.contains("unsupported") {
        print("Wallet does not support message signing")
    } else {
        print("Signing failed: \(error.localizedDescription)")
    }
}

Next Steps