Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt

Use this file to discover all available pages before exploring further.

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 a raw message

Use signRawMessage to sign raw bytes without the EIP-191 message prefix. This is useful when you need to sign pre-hashed data or raw byte payloads.
import DynamicSDKSwift
import Foundation

let sdk = DynamicSDK.instance()

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

// Usage
let wallet = sdk.wallets.userWallets.first!
let rawBytes = Data([0x48, 0x65, 0x6c, 0x6c, 0x6f])
await signRawMessage(wallet: wallet, rawMessage: rawBytes)

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