signTypedData
Signs EIP-712 typed data via ERC-7739 TypedDataSign
format.
This Action is suitable to sign messages for contracts (e.g. ERC-4337 Smart Accounts) that implement (or conform to) ERC-7739 (e.g. Solady's ERC1271.sol).
With the calculated signature, you can use verifyTypedData
to verify the signature
Usage
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(erc7739({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(erc7739())
export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
// @filename: example.js
// ---cut---
import { account, walletClient } from './config'
import { domain, types } from './data'
const signature = await walletClient.signTypedData({
// Account used for signing.
account,
domain,
types,
primaryType: 'Mail',
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
// Verifying contract address (e.g. ERC-4337 Smart Account).
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
})
Account and/or Verifier Hoisting
If you do not wish to pass an account
and/or verifier
to every signTypedData
, you can also hoist the Account and/or Verifier on the Wallet Client (see config.ts
).
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(erc7739({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(erc7739())
export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
// @filename: example.js
// ---cut---
import { walletClient } from './config'
import { domain, types } from './data'
const signature = await walletClient.signTypedData({
domain,
types,
primaryType: 'Mail',
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
})
Returns
The signed data.
Parameters
account
- Type:
Account | Address
Account to used to sign the typed data.
Accepts a JSON-RPC Account or Local Account (Private Key, etc).
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(erc7739({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(erc7739())
export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
// @filename: example.js
// ---cut---
import { walletClient } from './config'
const signature = await walletClient.signTypedData({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
domain: {
name: 'Ether Mail',
version: '1',
chainId: 1,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
})
domain
Type: TypedDataDomain
The typed data domain.
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(erc7739({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(erc7739())
export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
// @filename: example.js
// ---cut---
import { walletClient } from './config'
const signature = await walletClient.signTypedData({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
domain: {
name: 'Ether Mail',
version: '1',
chainId: 1,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
})
types
The type definitions for the typed data.
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(erc7739({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(erc7739())
export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
// @filename: example.js
// ---cut---
import { walletClient } from './config'
const signature = await walletClient.signTypedData({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
domain: {
name: 'Ether Mail',
version: '1',
chainId: 1,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
})
primaryType
Type: Inferred string
.
The primary type to extract from types
and use in value
.
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(erc7739({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(erc7739())
export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
// @filename: example.js
// ---cut---
import { walletClient } from './config'
const signature = await walletClient.signTypedData({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
domain: {
name: 'Ether Mail',
version: '1',
chainId: 1,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
})
message
Type: Inferred from types
& primaryType
.
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(erc7739({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(erc7739())
export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
// @filename: example.js
// ---cut---
import { walletClient } from './config'
const signature = await walletClient.signTypedData({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
domain: {
name: 'Ether Mail',
version: '1',
chainId: 1,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
})
verifier
- Type:
Address
The address of the verifying contract (e.g. a ERC-4337 Smart Account). Required if verifierDomain
is not passed.
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(erc7739({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(erc7739())
export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
// @filename: example.js
// ---cut---
import { walletClient } from './config'
const signature = await walletClient.signTypedData({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
domain: {
name: 'Ether Mail',
version: '1',
chainId: 1,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
})
verifierDomain
- Type:
TypedDataDomain
Account domain separator. Required if verifier
is not passed.
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(erc7739({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
// @filename: config.ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { erc7739 } from 'viem/experimental'
export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(erc7739())
export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
// @filename: example.js
// ---cut---
import { walletClient } from './config'
const signature = await walletClient.signTypedData({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
domain: {
name: 'Ether Mail',
version: '1',
chainId: 1,
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
verifierDomain: {
name: 'SoladyAccount',
version: '1',
chainId: 1,
verifyingContract: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
},
})