> ## 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 Typed Data (EIP-712)

> Sign EIP-712 structured data with an EVM wallet using Dynamic's Java SDK

## Overview

Use the Java SDK to sign [EIP-712](https://eips.ethereum.org/EIP-712) typed data with your server wallet. Typed data signing is used for permits, order signing, and other structured data that produces a recoverable signature.

`DynamicEvmWalletClient::signTypedData` takes the typed data as a JSON string (the standard EIP-712 envelope: `domain`, `types`, `primaryType`, `message`) and returns the `0x`-prefixed `r || s || v` signature.

## Prerequisites

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

## Sign Typed Data

```java theme={"system"}
import xyz.dynamic.waas.evm.opts.SignTypedDataOpts;

String typedDataJson = """
    {
      "domain": {
        "name": "My App",
        "version": "1",
        "chainId": 1
      },
      "types": {
        "EIP712Domain": [
          {"name": "name",    "type": "string"},
          {"name": "version", "type": "string"},
          {"name": "chainId", "type": "uint256"}
        ],
        "Message": [
          {"name": "content", "type": "string"},
          {"name": "from",    "type": "address"}
        ]
      },
      "primaryType": "Message",
      "message": {
        "content": "Hello!",
        "from": "0xYourWalletAddress"
      }
    }""";

String signature = client.signTypedData(SignTypedDataOpts.builder()
    .typedDataJson(typedDataJson)
    .walletProperties(walletProperties)
    .externalServerKeyShares(externalServerKeyShares)
    .build()
).join();

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

The method returns the serialized ECDSA signature as a `0x`-prefixed hex string (130 hex chars).

## ERC-2612 Permit Example

A common use case is signing ERC-2612 token permits, allowing gasless approvals:

```java theme={"system"}
String permitJson = """
    {
      "domain": {
        "name": "USD Coin",
        "version": "2",
        "chainId": 1,
        "verifyingContract": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
      },
      "types": {
        "EIP712Domain": [
          {"name": "name",              "type": "string"},
          {"name": "version",           "type": "string"},
          {"name": "chainId",           "type": "uint256"},
          {"name": "verifyingContract", "type": "address"}
        ],
        "Permit": [
          {"name": "owner",    "type": "address"},
          {"name": "spender",  "type": "address"},
          {"name": "value",    "type": "uint256"},
          {"name": "nonce",    "type": "uint256"},
          {"name": "deadline", "type": "uint256"}
        ]
      },
      "primaryType": "Permit",
      "message": {
        "owner":    "0xYourWalletAddress",
        "spender":  "0xSpenderAddress",
        "value":    "1000000",
        "nonce":    "0",
        "deadline": "1735689600"
      }
    }""";

String signature = client.signTypedData(SignTypedDataOpts.builder()
    .typedDataJson(permitJson)
    .walletProperties(walletProperties)
    .externalServerKeyShares(externalServerKeyShares)
    .build()
).join();
```

Large integers (`uint256`) should be serialized as JSON strings to avoid precision loss — Java's JSON parsers and the underlying web3j EIP-712 encoder both accept the string form.

## Verifying a typed-data signature

EIP-712 signatures recover the same way as EIP-191 — hash the typed-data digest, then run `ecrecover` against the signature. Use web3j's [`StructuredDataEncoder`](https://docs.web3j.io/4.11.0/transactions/transactions_and_smart_contracts/#using-eip-712-typed-structured-data-for-signing-and-verification):

```java theme={"system"}
import org.web3j.crypto.StructuredDataEncoder;
import org.web3j.crypto.Sign;
import org.web3j.crypto.Sign.SignatureData;
import org.web3j.crypto.Keys;
import org.web3j.utils.Numeric;

byte[] digest = new StructuredDataEncoder(typedDataJson).hashStructuredData();
byte[] sig    = Numeric.hexStringToByteArray(signature);

SignatureData sd = new SignatureData(
    sig[64],
    java.util.Arrays.copyOfRange(sig, 0, 32),
    java.util.Arrays.copyOfRange(sig, 32, 64)
);
String recovered = "0x" + Keys.getAddress(Sign.signedMessageHashToKey(digest, sd));

assert recovered.equalsIgnoreCase(walletProperties.accountAddress());
```

## Next Steps

* [Sign messages](/java/evm/sign-messages)
* [Sign transactions](/java/evm/sign-transaction)
* [Send transactions](/java/evm/send-transaction)
* [Delegated access](/java/evm/delegated-access)
