> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Sign typed data

`signTypedData` signs structured data following the EIP-712 [`eth_signTypedData_v4`](https://docs.metamask.io/wallet/reference/json-rpc-methods/eth_signtypeddata_v4/) standard. Unlike a [plain message signature](https://www.openfort.io/docs/products/embedded-wallet/swift/wallet/sign-message), typed data is a strongly typed object with a domain, so wallets and contracts can present and verify exactly what is being signed. It's the standard for gasless approvals (ERC-2612 permits), order signing, and most DeFi interactions.

A typed-data payload has three parts:

* **domain** — identifies the contract and chain the signature is scoped to (prevents replay across apps/chains).
* **types** — the schema of your message (field names and their types).
* **message** — the actual values being signed.

:::note
The wallet must be [`.ready`](https://www.openfort.io/docs/products/embedded-wallet/swift/wallet/state) before signing.
:::

## Usage

`signTypedData` returns the signature as a `String?` (`OFSignTypedDataResponse` is a typealias for `String`):

```swift
let domain = EIP712Domain(
    name: "Openfort",
    version: "0.5",
    chainId: 84532, // Base Sepolia — must match the verifying contract's chain
    verifyingContract: "0x9b5AB198e042fCF795E4a0Fa4269764A4E8037D2"
)

let types: [String: [EIP712TypeField]] = [
    "Mail": [
        EIP712TypeField(name: "from", type: "Person"),
        EIP712TypeField(name: "to", type: "Person"),
        EIP712TypeField(name: "content", type: "string"),
    ],
    "Person": [
        EIP712TypeField(name: "name", type: "string"),
        EIP712TypeField(name: "wallet", type: "address"),
    ],
]

let message = EIP712MailMessage(
    from: EIP712PersonMessage(name: "Alice", wallet: "0x2111111111111111111111111111111111111111"),
    to: EIP712PersonMessage(name: "Bob", wallet: "0x3111111111111111111111111111111111111111"),
    content: "Hello World!"
)

do {
    let signature = try await OFSDK.shared.signTypedData(
        params: OFSignTypedDataParams(domain: domain, types: types, message: message)
    )
    print("Signature: \(signature ?? "")")
} catch {
    print("Failed to sign typed data: \(error.localizedDescription)")
}
```

:::warning
The `chainId` in `domain` must match the chain the verifying contract lives on. A mismatch produces a valid-looking signature that the contract will reject.
:::

## Parameters

```swift
public struct OFSignTypedDataParams: OFCodableSendable {
    // Properties are internal — use the initializer.
    public init(domain: EIP712Domain, types: [String: [EIP712TypeField]] = [:], message: EIP712MailMessage)
}

public struct EIP712Domain: OFCodableSendable {
    let name: String
    let version: String
    let chainId: Int
    let verifyingContract: String
}

public struct EIP712TypeField: OFCodableSendable {
    let name: String
    let type: String
}

public struct EIP712MailMessage: OFCodableSendable {
    let from: EIP712PersonMessage
    let to: EIP712PersonMessage
    let content: String
}

public struct EIP712PersonMessage: OFCodableSendable {
    let name: String
    let wallet: String
}
```

:::note
The `message` shape (`EIP712MailMessage` / `EIP712PersonMessage`) models the canonical EIP-712 "Mail" example, and `OFSignTypedDataParams` only accepts that type. To sign a different schema, call `eth_signTypedData_v4` through the [Ethereum provider](https://www.openfort.io/docs/products/embedded-wallet/swift/wallet/ethereum-provider).
:::

## Returns

```swift
OFSignTypedDataResponse? // typealias for String? — the signature
```

## Next steps

* [Sign a message](https://www.openfort.io/docs/products/embedded-wallet/swift/wallet/sign-message) — for plain string signatures.
* [Send a transaction](https://www.openfort.io/docs/products/embedded-wallet/swift/wallet/send)
