Skip to main content

Overview

Message signing on Solana allows users to prove wallet ownership by signing arbitrary messages.

Prerequisites

Sign Message

import DynamicSDKSwift

let sdk = DynamicSDK.instance()

func signMessage(wallet: BaseWallet, message: String) async throws -> String {
    let signer = sdk.solana.createSigner(wallet: wallet)
    return try await signer.signMessage(message: message)
}

// Usage
let wallet = sdk.wallets.userWallets.first { $0.chain.uppercased() == "SOL" }!
let signature = try await signMessage(wallet: wallet, message: "Hello, Solana!")
print("Signature: \(signature)")

SwiftUI Example

import SwiftUI
import DynamicSDKSwift

struct SolanaSignView: View {
    let wallet: BaseWallet

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

    private let sdk = DynamicSDK.instance()

    var body: some View {
        VStack(spacing: 16) {
            Text("Wallet: \(formatAddress(wallet.address))")
                .font(.caption)

            TextField("Message to sign", text: $message)
                .textFieldStyle(.roundedBorder)

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

            if let signature = signature {
                VStack(alignment: .leading) {
                    Text("Signature:").font(.headline)
                    Text(signature)
                        .font(.system(.caption, design: .monospaced))
                        .textSelection(.enabled)
                    Button("Copy") {
                        UIPasteboard.general.string = signature
                    }
                }
                .padding()
                .background(Color.green.opacity(0.1))
                .cornerRadius(8)
            }

            if let error = error {
                Text(error).foregroundColor(.red).font(.caption)
            }
        }
        .padding()
        .navigationTitle("Sign Message")
    }

    private func formatAddress(_ address: String) -> String {
        "\(address.prefix(6))...\(address.suffix(4))"
    }

    private func signMessage() {
        isLoading = true
        error = nil

        Task { @MainActor in
            do {
                let signer = sdk.solana.createSigner(wallet: wallet)
                signature = try await signer.signMessage(message: message)
            } catch {
                self.error = error.localizedDescription
            }
            isLoading = false
        }
    }
}

Authentication Use Case

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

    let signer = sdk.solana.createSigner(wallet: wallet)
    let signature = try await signer.signMessage(message: message)

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

Sign Action Confirmation

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

    let signer = sdk.solana.createSigner(wallet: wallet)
    return try await signer.signMessage(message: message)
}

Verify Signature Data

Structure for sending to backend:
struct SolanaSignatureData {
    let message: String
    let signature: String
    let publicKey: String

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

// Usage
let signatureData = SolanaSignatureData(
    message: "Hello, Solana!",
    signature: signature,
    publicKey: wallet.address
)

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

Best Practices

1. Include Context

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

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

Sign to prove ownership of this wallet.

Wallet: \(wallet.address)
Nonce: 12345
Timestamp: \(Date().timeIntervalSince1970)
"""

2. Handle Errors

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

    if errorDesc.contains("rejected") || errorDesc.contains("denied") {
        print("User rejected the signature request")
    } else {
        print("Signing failed: \(error)")
    }
}

3. Clear Sensitive Data

var signature: String? = try await signer.signMessage(message: message)
// ... use signature ...
signature = nil  // Clear from memory

Error Handling

do {
    let signer = sdk.solana.createSigner(wallet: wallet)
    let signature = try await signer.signMessage(message: message)
    print("Message signed: \(signature)")
} catch {
    print("Failed to sign message: \(error)")
}

Next Steps