> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Sign Solana Messages

> Sign messages with Solana wallets using the Dynamic Android SDK.

## Overview

Sign messages to prove wallet ownership for authentication without transaction fees.

## Prerequisites

* Dynamic SDK initialized (see [Installation Guide](/kotlin/quickstart))
* User authenticated (see [Authentication Guide](/kotlin/authentication))
* Solana wallet available (see [Wallet Creation](/kotlin/wallet-creation))

## Sign a Message

```kotlin theme={"system"}
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.BaseWallet

val sdk = DynamicSDK.getInstance()

suspend fun signMessage(wallet: BaseWallet, message: String): String {
    return try {
        val signature = sdk.wallets.signMessage(wallet, message)
        println("Message signed successfully!")
        println("Signature: $signature")
        signature
    } catch (e: Exception) {
        println("Failed to sign message: ${e.message}")
        throw e
    }
}

// Usage
val wallet = sdk.wallets.userWallets.first { it.chain.uppercase() == "SOL" }
val signature = signMessage(wallet, "Hello, Dynamic!")
```

## Common Use Cases

### Authentication

```kotlin theme={"system"}
import java.util.UUID

data class AuthenticationRequest(
    val message: String,
    val nonce: String,
    val timestamp: Long
)

suspend fun authenticateWithSignature(wallet: BaseWallet): String {
    val nonce = UUID.randomUUID().toString()
    val timestamp = System.currentTimeMillis()
    val message = "Sign this message to authenticate with MyApp.\n\nNonce: $nonce\nTimestamp: $timestamp\n\nThis request will not trigger a blockchain transaction or cost any fees."
    return sdk.wallets.signMessage(wallet, message)
}
```

### Sign User Actions

```kotlin theme={"system"}
data class UserAction(
    val action: String,
    val walletAddress: String,
    val timestamp: Long
)

suspend fun signUserAction(wallet: BaseWallet, action: String): String {
    val timestamp = System.currentTimeMillis()
    val message = "Action: $action\nWallet: ${wallet.address}\nTimestamp: $timestamp"
    return sdk.wallets.signMessage(wallet, message)
}
```

### Proof of Ownership

```kotlin theme={"system"}
suspend fun proveWalletOwnership(wallet: BaseWallet): Pair<String, String> {
    val timestamp = System.currentTimeMillis()
    val message = "I own wallet ${wallet.address}\n\nTimestamp: $timestamp\n\nThis signature proves ownership without revealing my private key."
    val signature = sdk.wallets.signMessage(wallet, message)
    return Pair(message, signature)
}
```

### Off-Chain Signatures

```kotlin theme={"system"}
data class PermitSignature(
    val spender: String,
    val amount: String,
    val deadline: Long,
    val signature: String
)

suspend fun signOffChainPermit(
    wallet: BaseWallet,
    spender: String,
    amount: String,
    deadline: Long
): PermitSignature {
    val message = "Permit:\nSpender: $spender\nAmount: $amount\nDeadline: $deadline\n\nThis is an off-chain signature and will not incur transaction fees."
    val signature = sdk.wallets.signMessage(wallet, message)
    return PermitSignature(spender, amount, deadline, signature)
}
```

## Verify Signatures

While signature verification typically happens on the backend, here's how to structure the verification data:

```kotlin theme={"system"}
data class SignatureData(
    val message: String,
    val signature: String,
    val signerAddress: String,
    val timestamp: Long,
    val chain: String = "SOL"
) {
    fun toJson(): Map<String, Any> {
        return mapOf(
            "message" to message,
            "signature" to signature,
            "signer" to signerAddress,
            "timestamp" to timestamp,
            "chain" to chain
        )
    }
}

// Usage
suspend fun createSignatureData(wallet: BaseWallet, message: String): SignatureData {
    val signature = sdk.wallets.signMessage(wallet, message)

    return SignatureData(
        message = message,
        signature = signature,
        signerAddress = wallet.address,
        timestamp = System.currentTimeMillis()
    )
}

// Send to backend for verification
val signatureData = createSignatureData(wallet, "Hello, Dynamic!")
val jsonData = signatureData.toJson()
```

## Best Practices

* Always handle errors gracefully
* Include clear context in messages (wallet address, nonce, timestamp, purpose)
* Show loading states while waiting for signature
* Clear sensitive data when done
* Validate message format (not empty, reasonable length)
* Explain that signing is free and doesn't create transactions

## Error Handling

```kotlin theme={"system"}
sealed class SignatureError {
    data class UserRejected(val message: String) : SignatureError()
    data class UnsupportedWallet(val message: String) : SignatureError()
    data class NetworkError(val message: String) : SignatureError()
    data class InvalidMessage(val message: String) : SignatureError()
    data class Unknown(val message: String) : SignatureError()
}

fun parseSignatureError(e: Exception): SignatureError {
    val errorMessage = e.message?.lowercase() ?: ""

    return when {
        errorMessage.contains("rejected") || errorMessage.contains("denied") ->
            SignatureError.UserRejected("User rejected the signature request")
        errorMessage.contains("unsupported") ->
            SignatureError.UnsupportedWallet("Wallet does not support message signing")
        errorMessage.contains("network") ->
            SignatureError.NetworkError("Network error occurred")
        errorMessage.contains("invalid") ->
            SignatureError.InvalidMessage("Invalid message format")
        else ->
            SignatureError.Unknown("Signing failed: ${e.message}")
    }
}

// Usage in ViewModel
try {
    val signed = sdk.wallets.signMessage(wallet, _message.value)
    _signedMessage.value = signed
} catch (e: Exception) {
    val error = parseSignatureError(e)
    _errorMessage.value = when (error) {
        is SignatureError.UserRejected -> error.message
        is SignatureError.UnsupportedWallet -> error.message
        is SignatureError.NetworkError -> error.message
        is SignatureError.InvalidMessage -> error.message
        is SignatureError.Unknown -> error.message
    }
}
```

## User Consent Dialog

```kotlin theme={"system"}
@Composable
fun SignatureConsentDialog(
    message: String,
    walletAddress: String,
    onConfirm: () -> Unit,
    onDismiss: () -> Unit
) {
    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("Sign Message") },
        text = {
            Column {
                Text(
                    "You are about to sign the following message with your Solana wallet:",
                    style = MaterialTheme.typography.bodyMedium
                )

                Spacer(modifier = Modifier.height(8.dp))

                Card(
                    colors = CardDefaults.cardColors(
                        containerColor = MaterialTheme.colorScheme.surfaceVariant
                    )
                ) {
                    Text(
                        text = message,
                        modifier = Modifier.padding(12.dp),
                        style = MaterialTheme.typography.bodySmall
                    )
                }

                Spacer(modifier = Modifier.height(8.dp))

                Text(
                    "Wallet: ${walletAddress.take(8)}...${walletAddress.takeLast(8)}",
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant
                )

                Spacer(modifier = Modifier.height(8.dp))

                Text(
                    "This action is free and will not send a transaction.",
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant
                )
            }
        },
        confirmButton = {
            Button(onClick = onConfirm) {
                Text("Sign")
            }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) {
                Text("Cancel")
            }
        }
    )
}
```

## Signature Explanation UI

```kotlin theme={"system"}
@Composable
fun SignatureExplanation() {
    Card(
        modifier = Modifier.fillMaxWidth(),
        colors = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.primaryContainer
        )
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(
                "Why sign this message?",
                style = MaterialTheme.typography.titleSmall,
                color = MaterialTheme.colorScheme.onPrimaryContainer
            )
            Spacer(modifier = Modifier.height(8.dp))
            Text(
                "Signing proves you own this wallet without revealing your private keys or spending any SOL. It's free and doesn't create a blockchain transaction.",
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onPrimaryContainer
            )
        }
    }
}
```

## Troubleshooting

**User rejection**: Explain why signing is needed (verifies ownership, free, no transaction, private key stays secure)

**Unsupported wallet**: Check if wallet supports signing before requesting

**Long messages**: Warn users to review carefully if message exceeds 500 characters

## Security Considerations

* Never sign messages you don't understand
* Always display message content clearly before signing
* Malicious signatures could be used to impersonate you

## What's Next

* [Send Solana Transactions](/kotlin/wallets/solana/send-transactions) - Transfer SOL
* [Solana Connection](/kotlin/wallets/solana/connection) - RPC setup and configuration
* [EVM Message Signing](/kotlin/wallets/evm/message-signing) - Sign with EVM wallets
* [Authentication](/kotlin/authentication) - Authenticate users with signatures
