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