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.
Overview
Use the Java SDK to sign 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
Sign Typed Data
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:
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:
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