Overview
EIP-712 is a standard for signing typed structured data. It provides a secure way to sign complex data structures that are human-readable and verifiable on-chain.Prerequisites
- Dynamic SDK initialized (see Installation Guide)
- User authenticated (see Authentication Guide)
- EVM wallet available (see Wallet Creation)
Sign Typed Data
Copy
Ask AI
import DynamicSDKSwift
let sdk = DynamicSDK.instance()
func signTypedData(wallet: BaseWallet) async {
// Example EIP-712 typed data
let typedDataJson = """
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"}
],
"Message": [
{"name": "content", "type": "string"}
]
},
"primaryType": "Message",
"domain": {
"name": "Example App",
"version": "1",
"chainId": 1
},
"message": {
"content": "Hello, World!"
}
}
"""
do {
let signature = try await sdk.wallets.signTypedData(
wallet: wallet,
typedDataJson: typedDataJson
)
print("Typed data signed!")
print("Signature: \(signature)")
} catch {
print("Failed to sign typed data: \(error)")
}
}
Common EIP-712 Structures
Basic Message
Copy
Ask AI
let messageTypedData = """
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"}
],
"Mail": [
{"name": "from", "type": "string"},
{"name": "to", "type": "string"},
{"name": "contents", "type": "string"}
]
},
"primaryType": "Mail",
"domain": {
"name": "Example DApp",
"version": "1",
"chainId": 1
},
"message": {
"from": "Alice",
"to": "Bob",
"contents": "Hello!"
}
}
"""
ERC-20 Permit
Copy
Ask AI
func createPermitTypedData(
tokenName: String,
tokenAddress: String,
owner: String,
spender: String,
value: String,
nonce: Int,
deadline: Int,
chainId: Int
) -> String {
return """
{
"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",
"domain": {
"name": "\(tokenName)",
"version": "1",
"chainId": \(chainId),
"verifyingContract": "\(tokenAddress)"
},
"message": {
"owner": "\(owner)",
"spender": "\(spender)",
"value": "\(value)",
"nonce": \(nonce),
"deadline": \(deadline)
}
}
"""
}
// Usage
let permitData = createPermitTypedData(
tokenName: "USDC",
tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
owner: wallet.address,
spender: "0xSpenderAddress",
value: "1000000", // 1 USDC (6 decimals)
nonce: 0,
deadline: Int(Date().timeIntervalSince1970) + 3600,
chainId: 1
)
let signature = try await sdk.wallets.signTypedData(
wallet: wallet,
typedDataJson: permitData
)
NFT Transfer Authorization
Copy
Ask AI
func createNFTTransferTypedData(
nftContractAddress: String,
tokenId: String,
from: String,
to: String,
chainId: Int
) -> String {
return """
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"Transfer": [
{"name": "tokenId", "type": "uint256"},
{"name": "from", "type": "address"},
{"name": "to", "type": "address"}
]
},
"primaryType": "Transfer",
"domain": {
"name": "MyNFT",
"version": "1",
"chainId": \(chainId),
"verifyingContract": "\(nftContractAddress)"
},
"message": {
"tokenId": "\(tokenId)",
"from": "\(from)",
"to": "\(to)"
}
}
"""
}
Meta-Transaction
Copy
Ask AI
func createMetaTxTypedData(
from: String,
to: String,
value: String,
gas: String,
nonce: Int,
data: String,
chainId: Int
) -> String {
return """
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"}
],
"MetaTransaction": [
{"name": "from", "type": "address"},
{"name": "to", "type": "address"},
{"name": "value", "type": "uint256"},
{"name": "gas", "type": "uint256"},
{"name": "nonce", "type": "uint256"},
{"name": "data", "type": "bytes"}
]
},
"primaryType": "MetaTransaction",
"domain": {
"name": "MetaTxRelay",
"version": "1",
"chainId": \(chainId)
},
"message": {
"from": "\(from)",
"to": "\(to)",
"value": "\(value)",
"gas": "\(gas)",
"nonce": \(nonce),
"data": "\(data)"
}
}
"""
}
SwiftUI Example
Copy
Ask AI
import SwiftUI
import DynamicSDKSwift
struct SignTypedDataView: View {
let wallet: BaseWallet
@State private var signature: String?
@State private var isLoading = false
@State private var errorMessage: String?
private let sdk = DynamicSDK.instance()
private let exampleTypedData = """
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"}
],
"Mail": [
{"name": "from", "type": "string"},
{"name": "to", "type": "string"},
{"name": "contents", "type": "string"}
]
},
"primaryType": "Mail",
"domain": {
"name": "Example DApp",
"version": "1",
"chainId": 1
},
"message": {
"from": "Alice",
"to": "Bob",
"contents": "Hello!"
}
}
"""
var body: some View {
VStack(spacing: 16) {
Text("EIP-712 Typed Data")
.font(.headline)
Text(exampleTypedData)
.font(.system(.caption2, design: .monospaced))
.padding()
.background(Color(.systemGray6))
.cornerRadius(8)
Button("Sign Typed Data") {
signTypedData()
}
.disabled(isLoading)
if isLoading {
ProgressView()
}
if let signature = signature {
VStack(alignment: .leading) {
Text("Signature:")
.font(.caption)
Text(signature)
.font(.system(.caption2, design: .monospaced))
.lineLimit(3)
}
.padding()
.background(Color.green.opacity(0.1))
.cornerRadius(8)
}
if let error = errorMessage {
Text(error)
.foregroundColor(.red)
.font(.caption)
}
}
.padding()
}
private func signTypedData() {
isLoading = true
errorMessage = nil
signature = nil
Task { @MainActor in
do {
signature = try await sdk.wallets.signTypedData(
wallet: wallet,
typedDataJson: exampleTypedData
)
} catch {
errorMessage = "Failed to sign: \(error.localizedDescription)"
}
isLoading = false
}
}
}
Use Cases
Gasless Transactions
EIP-712 signatures enable gasless transactions where a relayer pays the gas:Copy
Ask AI
// 1. User signs typed data for the transaction
let metaTxData = createMetaTxTypedData(
from: wallet.address,
to: targetContract,
value: "0",
gas: "100000",
nonce: 0,
data: calldata,
chainId: 1
)
let signature = try await sdk.wallets.signTypedData(
wallet: wallet,
typedDataJson: metaTxData
)
// 2. Send signature to relayer
// Relayer executes transaction and pays gas
Token Approvals Without Gas
Copy
Ask AI
// Sign permit instead of sending approve transaction
let permitSignature = try await sdk.wallets.signTypedData(
wallet: wallet,
typedDataJson: permitData
)
// Contract can verify signature and grant approval without user paying gas
Off-Chain Order Books
Copy
Ask AI
// Sign order for DEX without on-chain transaction
let orderData = createOrderTypedData(
tokenIn: "0xUSDC",
tokenOut: "0xWETH",
amountIn: "1000000000",
amountOut: "500000000000000000",
deadline: Int(Date().timeIntervalSince1970) + 3600
)
let orderSignature = try await sdk.wallets.signTypedData(
wallet: wallet,
typedDataJson: orderData
)
// Submit signed order to off-chain order book
Best Practices
1. Validate Domain
Always include and validate the domain parameters:Copy
Ask AI
let typedData = """
{
"domain": {
"name": "Your App Name",
"version": "1",
"chainId": \(expectedChainId),
"verifyingContract": "\(contractAddress)"
},
...
}
"""
2. Include Nonces
Prevent replay attacks by including nonces:Copy
Ask AI
"message": {
"nonce": \(currentNonce),
...
}
3. Set Deadlines
Always include expiration timestamps:Copy
Ask AI
let deadline = Int(Date().timeIntervalSince1970) + 3600 // 1 hour
"message": {
"deadline": \(deadline),
...
}
4. Handle Errors
Copy
Ask AI
do {
let signature = try await sdk.wallets.signTypedData(
wallet: wallet,
typedDataJson: typedDataJson
)
} catch {
if error.localizedDescription.contains("rejected") {
print("User rejected the signature")
} else {
print("Signing failed: \(error)")
}
}
EIP-712 Structure
The typed data must follow this structure:Copy
Ask AI
{
"types": {
"EIP712Domain": [...],
"YourTypeName": [...]
},
"primaryType": "YourTypeName",
"domain": {
"name": "...",
"version": "...",
"chainId": ...,
"verifyingContract": "..."
},
"message": {
// Your data fields
}
}
Next Steps
- Message Signing - Sign plain text messages
- Smart Contract Interactions - Interact with contracts
- Send ETH Transactions - Send transactions