> ## 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.

# Message Signing

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

## Overview

Sign messages to prove wallet ownership for authentication and verification without transactions.

## Prerequisites

* Dynamic SDK initialized (see [Installation Guide](/kotlin/quickstart))
* User authenticated (see [Authentication Guide](/kotlin/authentication))
* EVM 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()
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"
    return sdk.wallets.signMessage(wallet, message)
}
```

### Signing 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)
}
```

### 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"
    val signature = sdk.wallets.signMessage(wallet, message)
    return PermitSignature(spender, amount, deadline, signature)
}
```

### Proof of Ownership

```kotlin theme={"system"}
suspend fun proveWalletOwnership(wallet: BaseWallet): String {
    val timestamp = System.currentTimeMillis()
    val message = "I own wallet ${wallet.address} at timestamp $timestamp"

    return sdk.wallets.signMessage(wallet, message)
}

// Usage in ViewModel
fun verifyOwnership() {
    viewModelScope.launch {
        try {
            val signature = proveWalletOwnership(wallet)
            _ownershipProof.value = signature
        } catch (e: Exception) {
            _errorMessage.value = "Failed to prove ownership: ${e.message}"
        }
    }
}
```

## Verify Signatures

While signature verification typically happens on the backend or smart contract, 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
) {
    fun toJson(): Map<String, Any> {
        return mapOf(
            "message" to message,
            "signature" to signature,
            "signer" to signerAddress,
            "timestamp" to timestamp
        )
    }
}

// Usage
val signatureData = SignatureData(
    message = "Hello, Dynamic!",
    signature = signature,
    signerAddress = wallet.address,
    timestamp = System.currentTimeMillis()
)

// Send to backend for verification
val jsonData = signatureData.toJson()
```

### Backend Verification Example

```kotlin theme={"system"}
// This would run on your backend server
import org.web3j.crypto.Keys
import org.web3j.crypto.Sign
import org.web3j.utils.Numeric

fun verifySignature(
    message: String,
    signature: String,
    expectedAddress: String
): Boolean {
    try {
        // Parse signature
        val signatureBytes = Numeric.hexStringToByteArray(signature)
        val r = signatureBytes.copyOfRange(0, 32)
        val s = signatureBytes.copyOfRange(32, 64)
        val v = signatureBytes[64]

        // Hash message with Ethereum prefix
        val messageHash = Sign.getEthereumMessageHash(message.toByteArray())

        // Recover address from signature
        val signData = Sign.SignatureData(v, r, s)
        val publicKey = Sign.signedMessageHashToKey(messageHash, signData)
        val recoveredAddress = "0x" + Keys.getAddress(publicKey)

        // Compare addresses (case-insensitive)
        return recoveredAddress.equals(expectedAddress, ignoreCase = true)
    } catch (e: Exception) {
        return false
    }
}
```

## Best Practices

* Always handle errors gracefully
* Include clear context in messages (nonce, timestamp, purpose)
* Show loading states while waiting for signature
* Clear sensitive data when done
* Validate message format (not empty, reasonable length)
* Never sign messages you don't understand

## 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 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(6)}...${walletAddress.takeLast(4)}",
                    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")
            }
        }
    )
}
```

## Troubleshooting

**User rejection**: Add clear explanation of why signing is needed (proves ownership, free, no transaction)

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

## What's Next

* [Typed Data Signing](/kotlin/wallets/evm/typed-data-signing) - Sign structured data (EIP-712)
* [Send ETH Transactions](/kotlin/wallets/evm/send-eth) - Send transactions
* [Smart Contract Interactions](/kotlin/wallets/evm/smart-contracts) - Interact with contracts
* [Authentication](/kotlin/authentication) - Authenticate users with signatures
