Skip to content

Commit

Permalink
feat: experimental solady erc1271 sign actions (#2413)
Browse files Browse the repository at this point in the history
* wip: solady

* Delete test/contracts/src/CounterfactualContractCall.sol

* wip: add factory + factoryData to verifyHash

* tweak

* tweak

* snaps

* wip: checkpoint

* docs

* docs

* size

* rebase

* size
  • Loading branch information
jxom committed Jun 19, 2024
1 parent 4b92979 commit 99ed745
Show file tree
Hide file tree
Showing 24 changed files with 2,430 additions and 117 deletions.
5 changes: 5 additions & 0 deletions .changeset/twenty-gorillas-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

**Experimental:** Added [Solady flavoured ERC-1271](https://github.com/Vectorized/solady/blob/678c9163550810b08f0ffb09624c9f7532392303/src/accounts/ERC1271.sol) `signMessage` & `signTypedData` for Smart Accounts that implement (or conform to) [Solady's `ERC1271.sol`](https://github.com/Vectorized/solady/blob/678c9163550810b08f0ffb09624c9f7532392303/src/accounts/ERC1271.sol#L110-L180).
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
{
"name": "viem (esm)",
"path": "./src/_esm/index.js",
"limit": "60.2 kB",
"limit": "60.5 kB",
"import": "*"
},
{
Expand Down Expand Up @@ -179,13 +179,13 @@
{
"name": "viem/siwe",
"path": "./src/_esm/siwe/index.js",
"limit": "30.5 kB",
"limit": "30.7 kB",
"import": "*"
},
{
"name": "viem/siwe (tree-shaking)",
"path": "./src/_esm/siwe/index.js",
"limit": "29.5 kB",
"limit": "29.6 kB",
"import": "{ verifySiweMessage }"
}
]
Expand Down
2 changes: 1 addition & 1 deletion site/pages/docs/utilities/verifyTypedData.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Verify that typed data was signed by the provided address.

:::warning[Warning]
This utility can only verify typed data that was signed by an Externally Owned Account (EOA).
To verify messages from Contract Accounts (& EOA), use the [`publicClient.verifyMessage` Action](/docs/actions/public/verifyMessage) instead.
To verify messages from Contract Accounts (& EOA), use the [`publicClient.verifyTypedData` Action](/docs/actions/public/verifyTypedData) instead.
:::

## Usage
Expand Down
196 changes: 196 additions & 0 deletions site/pages/experimental/solady/signMessage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
---
description: Signs a personal sign message via Solady's ERC-1271 format.
---

# signMessage

Signs an [EIP-191](https://eips.ethereum.org/EIPS/eip-191) personal sign message via Solady's [ERC-1271 `PersonalSign` format](https://github.com/Vectorized/solady/blob/678c9163550810b08f0ffb09624c9f7532392303/src/accounts/ERC1271.sol#L154-L166).

This Action is suitable to sign messages for contracts (e.g. ERC-4337 Smart Accounts) that implement (or conform to) Solady's [ERC1271.sol](https://github.com/Vectorized/solady/blob/main/src/accounts/ERC1271.sol).

With the calculated signature, you can use [`verifyMessage`](/docs/actions/public/verifyMessage) to verify the signature

## Usage

:::code-group

```ts twoslash [example.ts]
import { account, walletClient } from './config'

const signature_1 = await walletClient.signMessage({ // [!code focus:99]
// Account used for signing.
account,
message: 'hello world',
// Verifying contract address (e.g. ERC-4337 Smart Account).
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
})

const signature_2 = await walletClient.signMessage({
// Account used for signing.
account,
// Hex data representation of message.
message: { raw: '0x68656c6c6f20776f726c64' },
// Verifying contract address (e.g. ERC-4337 Smart Account)
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
})
```

```ts twoslash [config.ts] filename="config.ts"
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { soladyActions } from 'viem/experimental'

export const walletClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(soladyActions())

export const [account] = await walletClient.getAddresses()
// @log: ↑ JSON-RPC Account

// export const account = privateKeyToAccount(...)
// @log: ↑ Local Account
```

:::

## Account and/or Verifier Hoisting

If you do not wish to pass an `account` and/or `verifier` to every `signMessage`, you can also hoist the Account and/or Verifier on the Wallet Client (see `config.ts`).

[Learn more](/docs/clients/wallet#withaccount).

:::code-group

```ts twoslash [example.ts]
import { walletClient } from './config'

const signature = await walletClient.signMessage({ // [!code focus:99]
message: 'hello world',
})
```

```ts [config.ts (JSON-RPC Account)]
import { createWalletClient, custom } from 'viem'
import { soladyActions } from 'viem/experimental'

// Retrieve Account from an EIP-1193 Provider.
const [account] = await window.ethereum.request({
method: 'eth_requestAccounts'
})

export const walletClient = createWalletClient({
account,
transport: custom(window.ethereum!)
}).extend(soladyActions({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
```

```ts twoslash [config.ts (Local Account)] filename="config.ts"
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { soladyActions } from 'viem/experimental'

export const walletClient = createWalletClient({
account: privateKeyToAccount('0x...'),
transport: http()
}).extend(soladyActions({
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
}))
```

:::

## Returns

[`Hex`](/docs/glossary/types#hex)

The signed message.

## Parameters

### account

- **Type:** `Account | Address`

Account to used to sign the message.

Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts) or [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc).

```ts twoslash
import { walletClient } from './config'

const signature = await walletClient.signMessage({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus:1]
message: 'hello world',
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2'
})
```

### message

- **Type:** `string | { raw: Hex | ByteArray }`

Message to sign.

By default, viem signs the UTF-8 representation of the message.

```ts twoslash
import { walletClient } from './config'

const signature = await walletClient.signMessage({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
message: 'hello world', // [!code focus:1]
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2',
})
```

To sign the data representation of the message, you can use the `raw` attribute.

```ts twoslash
import { walletClient } from './config'

const signature = await walletClient.signMessage({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
message: { raw: '0x68656c6c6f20776f726c64' }, // [!code focus:1]
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2',
})
```

### verifier

- **Type:** `Address`

The address of the verifying contract (e.g. a ERC-4337 Smart Account). Required if `verifierDomain` is not passed.

```ts twoslash
import { walletClient } from './config'

const signature = await walletClient.signMessage({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
message: 'hello world',
verifier: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2', // [!code focus:1]
})
```

### verifierDomain

- **Type:** `TypedDataDomain`

Account domain separator. Required if `verifier` is not passed.

```ts twoslash
import { walletClient } from './config'

const signature = await walletClient.signMessage({
account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
message: 'hello world',
verifierDomain: { // [!code focus]
name: 'Mock4337Account', // [!code focus]
version: '1', // [!code focus]
chainId: 1, // [!code focus]
verifyingContract: '0xCB9fA1eA9b8A3bf422a8639f23Df77ea66020eC2' // [!code focus]
}, // [!code focus]
})
```
Loading

0 comments on commit 99ed745

Please sign in to comment.