> ## 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 EVM Messages

> Sign messages with EIP-191 using Dynamic's Java SDK for EVM chains

## Overview

`DynamicEvmWalletClient::signMessage` signs raw bytes using EIP-191 (`personal_sign`) and returns a `0x`-prefixed `r || s || v` signature (65 bytes / 130 hex chars). Message signing is commonly used for authentication, nonce verification, and data integrity checks.

The SDK also exposes an off-chain verifier — `DynamicEvmWalletClient.verifyMessageSignature(...)` — for recovering the signer locally without any MPC or network I/O.

## Prerequisites

* [Created an EVM wallet](/java/evm/create-wallet)

## Basic Message Signing

The Java SDK is stateless — every sign call requires `walletProperties` and `externalServerKeyShares`. Load both from where you persisted them at wallet creation time.

```java theme={"system"}
import java.util.List;
import xyz.dynamic.waas.ServerKeyShare;
import xyz.dynamic.waas.core.types.WalletProperties;
import xyz.dynamic.waas.evm.DynamicEvmWalletClient;
import xyz.dynamic.waas.opts.SignMessageOpts;

// Load the metadata + shares you persisted at creation time.
WalletProperties walletProperties = WalletProperties.fromJson(
    redis.get("wallet:" + accountAddress)
);
List<ServerKeyShare> externalServerKeyShares = ServerKeyShare.fromJsonList(
    vault.read("wallet:" + accountAddress + "/shares")
);

String signature = client.signMessage(SignMessageOpts.builder()
    .message("Hello, World!".getBytes(java.nio.charset.StandardCharsets.UTF_8))
    .walletProperties(walletProperties)
    .externalServerKeyShares(externalServerKeyShares)
    .build()
).join();

System.out.println("Signature: " + signature);
```

`signature` is a `0x`-prefixed hex string — pass it directly to any EIP-191 verifier (web3j, ethers, viem, etc).

## Verifying the signer locally

Because the signature is standard EIP-191, you can recover the signer with the SDK's static helper — no MPC ceremony, no network I/O:

```java theme={"system"}
boolean ok = DynamicEvmWalletClient.verifyMessageSignature(
    walletProperties.accountAddress(),
    "Hello, World!".getBytes(java.nio.charset.StandardCharsets.UTF_8),
    signature
);
assert ok : "signature does not match accountAddress";
```

`verifyMessageSignature` hashes the message via EIP-191, recovers the signer address from `(r || s || v)`, and compares it case-insensitively to `accountAddress`. Returns `false` on any structural failure (bad length, bad recovery, mismatch).

## Common Use Cases

### Authentication

```java theme={"system"}
String nonce   = String.valueOf(System.currentTimeMillis());
String message = "Sign this message to authenticate: " + nonce;

String signature = client.signMessage(SignMessageOpts.builder()
    .message(message.getBytes(java.nio.charset.StandardCharsets.UTF_8))
    .walletProperties(walletProperties)
    .externalServerKeyShares(externalServerKeyShares)
    .build()
).join();
```

### Data Integrity

```java theme={"system"}
String payloadJson = "{\"userId\":123,\"action\":\"transfer\",\"amount\":\"100\"}";

String signature = client.signMessage(SignMessageOpts.builder()
    .message(payloadJson.getBytes(java.nio.charset.StandardCharsets.UTF_8))
    .walletProperties(walletProperties)
    .externalServerKeyShares(externalServerKeyShares)
    .build()
).join();
```

## Next Steps

* [Sign typed data (EIP-712)](/java/evm/sign-typed-data)
* [Sign transactions](/java/evm/sign-transaction)
* [Send transactions](/java/evm/send-transaction)
* [Export the EVM private key](/java/evm/export-private-key)
* [Use delegated access](/java/evm/delegated-access)
