-
-
Notifications
You must be signed in to change notification settings - Fork 962
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: `getEip712Domain` action
- Loading branch information
Showing
14 changed files
with
422 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"viem": patch | ||
--- | ||
|
||
Added `getEip712Domain` Action. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
[submodule "contracts/lib/forge-std"] | ||
path = contracts/lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std | ||
[submodule "test/contracts/lib/solady"] | ||
path = test/contracts/lib/solady | ||
url = [email protected]:Vectorized/solady.git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
--- | ||
description: Reads the EIP-712 domain from a contract. | ||
--- | ||
|
||
# getEip712Domain | ||
|
||
Reads the EIP-712 domain from a contract, based on the [ERC-5267 specification](https://eips.ethereum.org/EIPS/eip-5267). | ||
|
||
## Usage | ||
|
||
:::code-group | ||
|
||
```ts twoslash [example.ts] | ||
import { publicClient } from './client' | ||
|
||
const { domain, extensions, fields } = await publicClient.getEip712Domain({ | ||
address: '0x57ba3ec8df619d4d243ce439551cce713bb17411', | ||
}) | ||
``` | ||
|
||
```ts [client.ts] filename="client.ts" | ||
import { createPublicClient, http } from 'viem' | ||
import { mainnet } from 'viem/chains' | ||
|
||
export const publicClient = createPublicClient({ | ||
chain: mainnet, | ||
transport: http() | ||
}) | ||
``` | ||
|
||
::: | ||
|
||
## Returns | ||
|
||
`GetEip712DomainReturnType` | ||
|
||
The EIP-712 domain (`domain`) for the contract, with `fields` and `extensions`, as per [ERC-5267](https://eips.ethereum.org/EIPS/eip-5267). | ||
|
||
## Parameters | ||
|
||
### address | ||
|
||
- **Type:** `string` | ||
|
||
The address of the contract to read the EIP-712 domain from. | ||
|
||
```ts twoslash | ||
// [!include ~/snippets/publicClient.ts] | ||
// ---cut--- | ||
const result = await publicClient.getEip712Domain({ | ||
address: '0x57ba3ec8df619d4d243ce439551cce713bb17411', // [!code focus] | ||
}) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { expect, test } from 'vitest' | ||
import { Mock4337AccountFactory } from '../../../test/contracts/generated.js' | ||
import { anvilMainnet } from '../../../test/src/anvil.js' | ||
import { accounts } from '../../../test/src/constants.js' | ||
import { deployMock4337Account } from '../../../test/src/utils.js' | ||
import { pad } from '../../utils/index.js' | ||
import { mine } from '../test/mine.js' | ||
import { writeContract } from '../wallet/writeContract.js' | ||
import { getEip712Domain } from './getEip712Domain.js' | ||
import { simulateContract } from './simulateContract.js' | ||
|
||
const client = anvilMainnet.getClient() | ||
|
||
test('default', async () => { | ||
const { factoryAddress } = await deployMock4337Account() | ||
|
||
const { result: address, request } = await simulateContract(client, { | ||
account: accounts[0].address, | ||
abi: Mock4337AccountFactory.abi, | ||
address: factoryAddress, | ||
functionName: 'createAccount', | ||
args: [accounts[0].address, pad('0x0')], | ||
}) | ||
await writeContract(client, request) | ||
await mine(client, { blocks: 1 }) | ||
|
||
expect( | ||
await getEip712Domain(client, { | ||
address, | ||
}), | ||
).toMatchInlineSnapshot(` | ||
{ | ||
"domain": { | ||
"chainId": 1, | ||
"name": "Mock4337Account", | ||
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000", | ||
"verifyingContract": "0x8a00708a83D977494139D21D618C6C2A71fA8ed1", | ||
"version": "1", | ||
}, | ||
"extensions": [], | ||
"fields": "0x0f", | ||
} | ||
`) | ||
}) | ||
|
||
test('error: non-existent', async () => { | ||
await expect(() => | ||
getEip712Domain(client, { | ||
address: '0x0000000000000000000000000000000000000000', | ||
}), | ||
).rejects.toThrowErrorMatchingInlineSnapshot( | ||
` | ||
[Eip712DomainNotFoundError: No EIP-712 domain found on contract "0x0000000000000000000000000000000000000000". | ||
Ensure that: | ||
- The contract is deployed at the address "0x0000000000000000000000000000000000000000". | ||
- \`eip712Domain()\` function exists on the contract. | ||
- \`eip712Domain()\` function matches signature to ERC-5267 specification. | ||
Version: [email protected]] | ||
`, | ||
) | ||
}) | ||
|
||
test('error: default', async () => { | ||
await expect(() => | ||
getEip712Domain(client, { | ||
address: '0x00000000000000000000000000000000000000000', | ||
}), | ||
).rejects.toThrowErrorMatchingInlineSnapshot( | ||
` | ||
[ContractFunctionExecutionError: Address "0x00000000000000000000000000000000000000000" is invalid. | ||
- Address must be a hex value of 20 bytes (40 hex characters). | ||
- Address must match its checksum counterpart. | ||
Raw Call Arguments: | ||
to: 0x00000000000000000000000000000000000000000 | ||
data: 0x84b0196e | ||
Contract Call: | ||
address: 0x0000000000000000000000000000000000000000 | ||
function: eip712Domain() | ||
Docs: https://viem.sh/docs/contract/readContract | ||
Version: [email protected]] | ||
`, | ||
) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import type { Address, TypedDataDomain } from 'abitype' | ||
import type { Client } from '../../clients/createClient.js' | ||
import type { Transport } from '../../clients/transports/createTransport.js' | ||
import { | ||
Eip712DomainNotFoundError, | ||
type Eip712DomainNotFoundErrorType, | ||
} from '../../errors/eip712.js' | ||
import type { ErrorType } from '../../errors/utils.js' | ||
import type { Hex } from '../../types/misc.js' | ||
import { getAction } from '../../utils/getAction.js' | ||
import { type ReadContractErrorType, readContract } from './readContract.js' | ||
|
||
export type GetEip712DomainParameters = { | ||
address: Address | ||
} | ||
|
||
export type GetEip712DomainReturnType = { | ||
domain: TypedDataDomain | ||
fields: Hex | ||
extensions: readonly bigint[] | ||
} | ||
|
||
export type GetEip712DomainErrorType = | ||
| Eip712DomainNotFoundErrorType | ||
| ReadContractErrorType | ||
| ErrorType | ||
|
||
/** | ||
* Reads the EIP-712 domain from a contract, based on the ERC-5267 specification. | ||
* | ||
* @param client - A {@link Client} instance. | ||
* @param parameters - The parameters of the action. {@link GetEip712DomainParameters} | ||
* @returns The EIP-712 domain, fields, and extensions. {@link GetEip712DomainReturnType} | ||
* | ||
* @example | ||
* ```ts | ||
* import { createPublicClient, http, getEip712Domain } from 'viem' | ||
* import { mainnet } from 'viem/chains' | ||
* | ||
* const client = createPublicClient({ | ||
* chain: mainnet, | ||
* transport: http(), | ||
* }) | ||
* | ||
* const domain = await getEip712Domain(client, { | ||
* address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', | ||
* }) | ||
* // { | ||
* // domain: { | ||
* // name: 'ExampleContract', | ||
* // version: '1', | ||
* // chainId: 1, | ||
* // verifyingContract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', | ||
* // }, | ||
* // fields: '0x0f', | ||
* // extensions: [], | ||
* // } | ||
* ``` | ||
*/ | ||
export async function getEip712Domain( | ||
client: Client<Transport>, | ||
parameters: GetEip712DomainParameters, | ||
): Promise<GetEip712DomainReturnType> { | ||
const { address } = parameters | ||
|
||
try { | ||
const [ | ||
fields, | ||
name, | ||
version, | ||
chainId, | ||
verifyingContract, | ||
salt, | ||
extensions, | ||
] = await getAction( | ||
client, | ||
readContract, | ||
'readContract', | ||
)({ | ||
abi, | ||
address, | ||
functionName: 'eip712Domain', | ||
}) | ||
|
||
return { | ||
domain: { | ||
name, | ||
version, | ||
chainId: Number(chainId), | ||
verifyingContract, | ||
salt, | ||
}, | ||
extensions, | ||
fields, | ||
} | ||
} catch (e) { | ||
const error = e as ReadContractErrorType | ||
if ( | ||
error.name === 'ContractFunctionExecutionError' && | ||
error.cause.name === 'ContractFunctionZeroDataError' | ||
) { | ||
throw new Eip712DomainNotFoundError({ address }) | ||
} | ||
throw error | ||
} | ||
} | ||
|
||
const abi = [ | ||
{ | ||
inputs: [], | ||
name: 'eip712Domain', | ||
outputs: [ | ||
{ name: 'fields', type: 'bytes1' }, | ||
{ name: 'name', type: 'string' }, | ||
{ name: 'version', type: 'string' }, | ||
{ name: 'chainId', type: 'uint256' }, | ||
{ name: 'verifyingContract', type: 'address' }, | ||
{ name: 'salt', type: 'bytes32' }, | ||
{ name: 'extensions', type: 'uint256[]' }, | ||
], | ||
stateMutability: 'view', | ||
type: 'function', | ||
}, | ||
] as const |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.