FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

View File

@@ -0,0 +1,156 @@
// TODO(v3): Remove this.
import type { Abi, AbiStateMutability, Address, Narrow } from 'abitype'
import {
type SendCallsErrorType,
type SendCallsParameters,
type SendCallsReturnType,
sendCalls,
} from '../../../actions/wallet/sendCalls.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Account, GetAccountParameter } from '../../../types/account.js'
import type { Chain, GetChainParameter } from '../../../types/chain.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
GetValue,
UnionWiden,
Widen,
} from '../../../types/contract.js'
import type { MulticallContracts } from '../../../types/multicall.js'
import {
type EncodeFunctionDataErrorType,
encodeFunctionData,
} from '../../../utils/abi/encodeFunctionData.js'
import { getAction } from '../../../utils/getAction.js'
export type WriteContractsParameters<
contracts extends
readonly unknown[] = readonly WriteContractFunctionParameters[],
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
> = Pick<
SendCallsParameters<chain, account, chainOverride>,
'capabilities' | 'version'
> & {
contracts: MulticallContracts<
Narrow<contracts>,
{ mutability: AbiStateMutability }
>
} & GetAccountParameter<account> &
GetChainParameter<chain, chainOverride>
export type WriteContractsReturnType = SendCallsReturnType
export type WriteContractsErrorType =
| EncodeFunctionDataErrorType
| SendCallsErrorType
| ErrorType
/**
* Requests for the wallet to sign and broadcast a batch of write contract calls (transactions) to the network.
*
* - Docs: https://viem.sh/experimental/eip5792/writeContracts
*
* @param client - Client to use
* @param parameters - {@link WriteContractsParameters}
* @returns Unique identifier for the call batch. {@link WriteContractsReturnType}
*
* @example
* import { createPublicClient, http, parseAbi } from 'viem'
* import { mainnet } from 'viem/chains'
* import { writeContracts } from 'viem/experimental'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const abi = parseAbi([
* 'function approve(address, uint256) returns (bool)',
* 'function transferFrom(address, address, uint256) returns (bool)',
* ])
* const id = await writeContracts(client, {
* contracts: [
* {
* address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
* abi,
* functionName: 'approve',
* args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', 100n],
* },
* {
* address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
* abi,
* functionName: 'transferFrom',
* args: [
* '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC',
* '0x0000000000000000000000000000000000000000',
* 100n
* ],
* },
* ],
* })
*/
export async function writeContracts<
const contracts extends readonly unknown[],
chain extends Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain>,
parameters: WriteContractsParameters<
contracts,
chain,
account,
chainOverride
>,
): Promise<WriteContractsReturnType> {
const contracts = parameters.contracts as WriteContractFunctionParameters[]
const calls = contracts.map((contract) => {
const { address, abi, functionName, args, value } = contract
return {
data: encodeFunctionData({
abi,
functionName,
args,
}),
to: address,
value,
} satisfies SendCallsParameters['calls'][number]
})
return getAction(
client,
sendCalls,
'sendCalls',
)({ ...parameters, calls } as SendCallsParameters)
}
export type WriteContractFunctionParameters<
abi extends Abi | readonly unknown[] = Abi,
mutability extends AbiStateMutability = AbiStateMutability,
functionName extends ContractFunctionName<
abi,
mutability
> = ContractFunctionName<abi, mutability>,
args extends ContractFunctionArgs<
abi,
mutability,
functionName
> = ContractFunctionArgs<abi, mutability, functionName>,
///
allFunctionNames = ContractFunctionName<abi, mutability>,
allArgs = ContractFunctionArgs<abi, mutability, functionName>,
// when `args` is inferred to `readonly []` ("inputs": []) or `never` (`abi` declared as `Abi` or not inferrable), allow `args` to be optional.
// important that both branches return same structural type
> = {
address: Address
abi: abi
functionName:
| allFunctionNames // show all options
| (functionName extends allFunctionNames ? functionName : never) // infer value
args?: (abi extends Abi ? UnionWiden<args> : never) | allArgs | undefined
} & (readonly [] extends allArgs ? {} : { args: Widen<args> }) &
GetValue<abi, functionName>

View File

@@ -0,0 +1,274 @@
// TODO(v3): Remove this.
import {
type GetCallsStatusParameters,
type GetCallsStatusReturnType,
getCallsStatus,
} from '../../../actions/wallet/getCallsStatus.js'
import {
type GetCapabilitiesParameters,
type GetCapabilitiesReturnType,
getCapabilities,
} from '../../../actions/wallet/getCapabilities.js'
import {
type SendCallsParameters,
type SendCallsReturnType,
sendCalls,
} from '../../../actions/wallet/sendCalls.js'
import {
type ShowCallsStatusParameters,
type ShowCallsStatusReturnType,
showCallsStatus,
} from '../../../actions/wallet/showCallsStatus.js'
import {
type WaitForCallsStatusParameters,
type WaitForCallsStatusReturnType,
waitForCallsStatus,
} from '../../../actions/wallet/waitForCallsStatus.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Account } from '../../../types/account.js'
import type { Chain } from '../../../types/chain.js'
import {
type WriteContractsParameters,
type WriteContractsReturnType,
writeContracts,
} from '../actions/writeContracts.js'
export type Eip5792Actions<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
> = {
/**
* Returns the status of a call batch that was sent via `sendCalls`.
*
* - Docs: https://viem.sh/experimental/eip5792/getCallsStatus
* - JSON-RPC Methods: [`wallet_getCallsStatus`](https://eips.ethereum.org/EIPS/eip-5792)
*
* @param client - Client to use
* @returns Status of the calls. {@link GetCallsStatusReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { eip5792Actions } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(eip5792Actions())
*
* const { receipts, status } = await client.getCallsStatus({ id: '0xdeadbeef' })
*/
getCallsStatus: (
parameters: GetCallsStatusParameters,
) => Promise<GetCallsStatusReturnType>
/**
* Extract capabilities that a connected wallet supports (e.g. paymasters, session keys, etc).
*
* - Docs: https://viem.sh/experimental/eip5792/getCapabilities
* - JSON-RPC Methods: [`wallet_getCapabilities`](https://eips.ethereum.org/EIPS/eip-5792)
*
* @param client - Client to use
* @returns The wallet's capabilities. {@link GetCapabilitiesReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { eip5792Actions } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(eip5792Actions())
*
* const capabilities = await client.getCapabilities({
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* })
*/
getCapabilities: (
parameters?: GetCapabilitiesParameters,
) => Promise<GetCapabilitiesReturnType>
/**
* Requests the connected wallet to send a batch of calls.
*
* - Docs: https://viem.sh/docs/actions/wallet/sendCalls
* - JSON-RPC Methods: [`wallet_sendCalls`](https://eips.ethereum.org/EIPS/eip-5792)
*
* @param client - Client to use
* @returns Transaction identifier. {@link SendCallsReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { eip5792Actions } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(eip5792Actions())
*
* const id = await client.sendCalls({
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* calls: [
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 69420n,
* },
* ],
* })
*/
sendCalls: <
const calls extends readonly unknown[],
chainOverride extends Chain | undefined = undefined,
>(
parameters: SendCallsParameters<chain, account, chainOverride, calls>,
) => Promise<SendCallsReturnType>
/**
* Requests for the wallet to show information about a call batch
* that was sent via `sendCalls`.
*
* - Docs: https://viem.sh/experimental/eip5792/showCallsStatus
* - JSON-RPC Methods: [`wallet_showCallsStatus`](https://eips.ethereum.org/EIPS/eip-5792)
*
* @param client - Client to use
* @returns Displays status of the calls in wallet. {@link ShowCallsStatusReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { eip5792Actions } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(eip5792Actions())
*
* await client.showCallsStatus({ id: '0xdeadbeef' })
*/
showCallsStatus: (
parameters: ShowCallsStatusParameters,
) => Promise<ShowCallsStatusReturnType>
/**
* Waits for the status & receipts of a call bundle that was sent via `sendCalls`.
*
* - Docs: https://viem.sh/experimental/eip5792/waitForCallsStatus
* - JSON-RPC Methods: [`wallet_getCallsStatus`](https://eips.ethereum.org/EIPS/eip-5792)
*
* @param client - Client to use
* @param parameters - {@link WaitForCallsStatusParameters}
* @returns Status & receipts of the call bundle. {@link WaitForCallsStatusReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { waitForCallsStatus } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
*
* const { receipts, status } = await waitForCallsStatus(client, { id: '0xdeadbeef' })
*/
waitForCallsStatus: (
parameters: WaitForCallsStatusParameters,
) => Promise<WaitForCallsStatusReturnType>
/**
* Requests for the wallet to sign and broadcast a batch of write contract calls (transactions) to the network.
*
* - Docs: https://viem.sh/experimental/eip5792/writeContracts
*
* @param client - Client to use
* @param parameters - {@link WriteContractsParameters}
* @returns Unique identifier for the call batch. {@link WriteContractsReturnType}
*
* @example
* import { createPublicClient, http, parseAbi } from 'viem'
* import { mainnet } from 'viem/chains'
* import { writeContracts } from 'viem/experimental'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const abi = parseAbi([
* 'function approve(address, uint256) returns (bool)',
* 'function transferFrom(address, address, uint256) returns (bool)',
* ])
* const id = await client.writeContracts({
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* contracts: [
* {
* address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
* abi,
* functionName: 'approve',
* args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', 100n],
* },
* {
* address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
* abi,
* functionName: 'transferFrom',
* args: [
* '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC',
* '0x0000000000000000000000000000000000000000',
* 100n
* ],
* },
* ],
* })
*/
writeContracts: <
const contracts extends readonly unknown[],
chainOverride extends Chain | undefined = undefined,
>(
parameters: WriteContractsParameters<
contracts,
chain,
account,
chainOverride
>,
) => Promise<WriteContractsReturnType>
}
/**
* A suite of EIP-5792 Wallet Actions.
*
* - Docs: https://viem.sh/experimental
*
* @example
* import { createPublicClient, createWalletClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { eip5792Actions } from 'viem/experimental'
*
* const walletClient = createWalletClient({
* chain: mainnet,
* transport: http(),
* }).extend(eip5792Actions())
*
* const hash = await walletClient.sendCalls({...})
*/
export function eip5792Actions() {
return <
transport extends Transport,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
>(
client: Client<transport, chain, account>,
): Eip5792Actions<chain, account> => {
return {
getCallsStatus: (parameters) => getCallsStatus(client, parameters),
getCapabilities: ((parameters: any) =>
getCapabilities(client as any, parameters)) as any,
sendCalls: (parameters) => sendCalls(client, parameters),
showCallsStatus: (parameters) => showCallsStatus(client, parameters),
waitForCallsStatus: (parameters) =>
waitForCallsStatus(client, parameters),
writeContracts: (parameters) => writeContracts(client, parameters),
}
}
}

View File

@@ -0,0 +1,192 @@
import type { Address } from 'abitype'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Account } from '../../../types/account.js'
import type { WalletGrantPermissionsReturnType } from '../../../types/eip1193.js'
import type { Hex } from '../../../types/misc.js'
import type { OneOf } from '../../../types/utils.js'
import { numberToHex, parseAccount } from '../../../utils/index.js'
import type { Permission } from '../types/permission.js'
import type { Signer } from '../types/signer.js'
export type GrantPermissionsParameters = {
/** Timestamp (in seconds) that specifies the time by which this session MUST expire. */
expiry: number
/** Set of permissions to grant to the user. */
permissions: readonly Permission[]
} & OneOf<
| {
/** Signer to assign the permissions to. */
signer?: Signer | undefined
}
| {
/** Account to assign the permissions to. */
account?: Address | Account | undefined
}
>
export type GrantPermissionsReturnType = {
/** Timestamp (in seconds) that specifies the time by which this session MUST expire. */
expiry: number
/** ERC-4337 Factory to deploy smart contract account. */
factory?: Hex | undefined
/** Calldata to use when calling the ERC-4337 Factory. */
factoryData?: string | undefined
/** Set of granted permissions. */
grantedPermissions: readonly Permission[]
/** Permissions identifier. */
permissionsContext: string
/** Signer attached to the permissions. */
signerData?:
| {
userOpBuilder?: Hex | undefined
submitToAddress?: Hex | undefined
}
| undefined
}
/**
* Request permissions from a wallet to perform actions on behalf of a user.
*
* - Docs: https://viem.sh/experimental/erc7715/grantPermissions
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { grantPermissions } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
*
* const result = await grantPermissions(client, {
* expiry: 1716846083638,
* permissions: [
* {
* type: 'native-token-transfer',
* data: {
* ticker: 'ETH',
* },
* policies: [
* {
* type: 'token-allowance',
* data: {
* allowance: parseEther('1'),
* },
* }
* ],
* required: true,
* },
* ],
* })
*/
export async function grantPermissions(
client: Client<Transport>,
parameters: GrantPermissionsParameters,
): Promise<GrantPermissionsReturnType> {
const { account, expiry, permissions, signer } = parameters
const result = await client.request(
{
method: 'wallet_grantPermissions',
params: [
formatParameters({ account, expiry, permissions, signer } as any),
],
},
{ retryCount: 0 },
)
return formatRequest(result) as GrantPermissionsReturnType
}
function formatParameters(parameters: GrantPermissionsParameters) {
const { expiry, permissions, signer: signer_ } = parameters
const account = parameters.account
? parseAccount(parameters.account)
: undefined
const signer = (() => {
if (!account && !signer_) return undefined
// JSON-RPC Account as signer.
if (account?.type === 'json-rpc')
return {
type: 'wallet',
}
// Local Account as signer.
if (account?.type === 'local')
return {
type: 'account',
data: {
id: account.address,
},
}
// ERC-7715 Signer as signer.
return signer_
})()
return {
expiry,
permissions: permissions.map((permission) => ({
...permission,
policies: permission.policies.map((policy) => {
const data = (() => {
if (policy.type === 'token-allowance')
return {
allowance: numberToHex(policy.data.allowance),
}
if (policy.type === 'gas-limit')
return {
limit: numberToHex(policy.data.limit),
}
return policy.data
})()
return {
data,
type:
typeof policy.type === 'string' ? policy.type : policy.type.custom,
}
}),
required: permission.required ?? false,
type:
typeof permission.type === 'string'
? permission.type
: permission.type.custom,
})),
...(signer ? { signer } : {}),
}
}
function formatRequest(result: WalletGrantPermissionsReturnType) {
return {
expiry: result.expiry,
...(result.factory ? { factory: result.factory } : {}),
...(result.factoryData ? { factoryData: result.factoryData } : {}),
grantedPermissions: result.grantedPermissions.map((permission) => ({
...permission,
policies: permission.policies.map((policy) => {
const data = (() => {
if (policy.type === 'token-allowance')
return {
allowance: BigInt((policy.data as any).allowance),
}
if (policy.type === 'gas-limit')
return {
limit: BigInt((policy.data as any).limit),
}
return policy.data
})()
return {
data,
type: policy.type,
}
}),
})),
permissionsContext: result.permissionsContext,
...(result.signerData ? { signerData: result.signerData } : {}),
}
}

View File

@@ -0,0 +1,80 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Account } from '../../../types/account.js'
import type { Chain } from '../../../types/chain.js'
import {
type GrantPermissionsParameters,
type GrantPermissionsReturnType,
grantPermissions,
} from '../actions/grantPermissions.js'
export type Erc7715Actions = {
/**
* Request permissions from a wallet to perform actions on behalf of a user.
*
* - Docs: https://viem.sh/experimental/erc7715/grantPermissions
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7715Actions } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(erc7715Actions())
*
* const result = await client.grantPermissions({
* expiry: 1716846083638,
* permissions: [
* {
* type: 'contract-call',
* data: {
* address: '0x0000000000000000000000000000000000000000',
* },
* },
* {
* type: 'native-token-limit',
* data: {
* amount: 69420n,
* },
* required: true,
* },
* ],
* })
*/
grantPermissions: (
parameters: GrantPermissionsParameters,
) => Promise<GrantPermissionsReturnType>
}
/**
* A suite of ERC-7715 Wallet Actions.
*
* - Docs: https://viem.sh/experimental
*
* @example
* import { createPublicClient, createWalletClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7715Actions } from 'viem/experimental'
*
* const walletClient = createWalletClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7715Actions())
*
* const result = await walletClient.grantPermissions({...})
*/
export function erc7715Actions() {
return <
transport extends Transport,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
>(
client: Client<transport, chain, account>,
): Erc7715Actions => {
return {
grantPermissions: (parameters) => grantPermissions(client, parameters),
}
}
}

View File

@@ -0,0 +1,53 @@
import type { Address } from 'abitype'
import type { OneOf } from '../../../types/utils.js'
import type { Policy } from './policy.js'
/** @internal */
export type CustomPermission<data = unknown, type = { custom: string }> = {
data: data
type: type
}
/** @internal */
export type NativeTokenTransferPermission = {
type: 'native-token-transfer'
data: {
/** Native token ticker (e.g. ETH). */
ticker: string
}
}
/** @internal */
export type Erc20TokenTransferPermission = {
type: 'erc20-token-transfer'
data: {
/** ERC20 address. */
address: Address
/** Native token ticker (e.g. ETH). */
ticker: string
}
}
/** @internal */
export type ContractCallPermission = {
type: 'contract-call'
data: {
/** Contract address. */
address: Address
/** Set of contract signatures to permit. */
calls: string[]
}
}
export type Permission<uint256 = bigint> = OneOf<
| NativeTokenTransferPermission
| Erc20TokenTransferPermission
| ContractCallPermission
| CustomPermission
> & {
/** Set of policies for the permission. */
policies: readonly Policy<uint256>[]
/** Whether or not the wallet must grant the permission. */
required?: boolean | undefined
}

43
node_modules/viem/experimental/erc7715/types/policy.ts generated vendored Normal file
View File

@@ -0,0 +1,43 @@
import type { OneOf } from '../../../types/utils.js'
/** @internal */
export type CustomPolicy<data = unknown> = {
data: data
type: { custom: string }
}
/** @internal */
export type TokenAllowancePolicy<uint256 = bigint> = {
type: 'token-allowance'
data: {
/** Token allowance (in wei). */
allowance: uint256
}
}
/** @internal */
export type GasLimitPolicy<uint256 = bigint> = {
type: 'gas-limit'
data: {
/** Gas limit (in wei). */
limit: uint256
}
}
/** @internal */
export type RateLimitPolicy = {
type: 'rate-limit'
data: {
/** Number of times during each interval. */
count: number
/** Interval (in seconds). */
interval: number
}
}
export type Policy<amount = bigint> = OneOf<
| TokenAllowancePolicy<amount>
| GasLimitPolicy<amount>
| RateLimitPolicy
| CustomPolicy
>

35
node_modules/viem/experimental/erc7715/types/signer.ts generated vendored Normal file
View File

@@ -0,0 +1,35 @@
import type { Address } from 'abitype'
import type { OneOf } from '../../../types/utils.js'
/** @internal */
export type AccountSigner = {
type: 'account'
data: {
id: Address
}
}
/** @internal */
export type KeySigner = {
type: 'key'
data: {
id: string
}
}
/** @internal */
export type MultiKeySigner = {
type: 'keys'
data: {
ids: string[]
}
}
/** @internal */
export type WalletSigner = {
type: 'wallet'
}
export type Signer = OneOf<
AccountSigner | KeySigner | MultiKeySigner | WalletSigner
>

View File

@@ -0,0 +1,149 @@
import type { Address, TypedDataDomain } from 'abitype'
import type { Account } from '../../../accounts/types.js'
import { parseAccount } from '../../../accounts/utils/parseAccount.js'
import {
type GetEip712DomainParameters,
getEip712Domain,
} from '../../../actions/public/getEip712Domain.js'
import { signTypedData } from '../../../actions/wallet/signTypedData.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import { AccountNotFoundError } from '../../../errors/account.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { GetAccountParameter } from '../../../types/account.js'
import type { Chain } from '../../../types/chain.js'
import type { Hex, SignableMessage } from '../../../types/misc.js'
import type { OneOf, RequiredBy } from '../../../types/utils.js'
import { getAction } from '../../../utils/getAction.js'
import { toPrefixedMessage } from '../../../utils/signature/toPrefixedMessage.js'
import type { GetVerifierParameter } from '../types.js'
export type SignMessageParameters<
account extends Account | undefined = Account | undefined,
accountOverride extends Account | undefined = Account | undefined,
verifier extends Address | undefined = Address | undefined,
> = Pick<GetEip712DomainParameters, 'factory' | 'factoryData'> &
GetAccountParameter<account, accountOverride> & {
message: SignableMessage
} & OneOf<
| {
verifierDomain: RequiredBy<
TypedDataDomain,
'chainId' | 'name' | 'verifyingContract' | 'version'
>
verifier?: undefined
}
| (GetVerifierParameter<verifier> & {
verifierDomain?:
| RequiredBy<
TypedDataDomain,
'chainId' | 'name' | 'verifyingContract' | 'version'
>
| undefined
})
>
export type SignMessageReturnType = Hex
export type SignMessageErrorType = ErrorType
/**
* Signs a [EIP-191](https://eips.ethereum.org/EIPS/eip-191) personal sign message via [ERC-7739 `PersonalSign` format](https://eips.ethereum.org/EIPS/eip-7702).
*
* This Action is suitable to sign messages for Smart Accounts that implement (or conform to) [ERC-7739](https://eips.ethereum.org/EIPS/eip-7702) (e.g. Solady's [ERC1271.sol](https://github.com/Vectorized/solady/blob/main/src/accounts/ERC1271.sol)).
*
* - Docs: https://viem.sh/experimental/erc7739/signMessage
*
* With the calculated signature, you can:
* - use [`verifyMessage`](https://viem.sh/docs/utilities/verifyMessage) to verify the signature,
*
* @param client - Client to use
* @param parameters - {@link SignMessageParameters}
* @returns The signed message. {@link SignMessageReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { signMessage } from 'viem/experimental/erc7739'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
*
* const signature = await signMessage(client, {
* account: '0xE8Df82fA4E10e6A12a9Dab552bceA2acd26De9bb',
* message: 'hello world',
* verifier: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* })
*
* @example
* // Account Hoisting
* import { createWalletClient, custom } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { signMessage } from 'viem/experimental/erc7739'
*
* const client = createWalletClient({
* account: '0xE8Df82fA4E10e6A12a9Dab552bceA2acd26De9bb',
* chain: mainnet,
* transport: custom(window.ethereum),
* })
*
* const signature = await signMessage(client, {
* message: 'hello world',
* verifier: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* })
*/
export async function signMessage<
chain extends Chain | undefined,
account extends Account | undefined,
accountOverride extends Account | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: SignMessageParameters<account, accountOverride>,
): Promise<SignMessageReturnType> {
const {
account: account_ = client.account,
factory,
factoryData,
message,
verifier,
} = parameters
if (!account_)
throw new AccountNotFoundError({
docsPath: '/experimental/erc7739/signMessage',
})
const account = parseAccount(account_)
const { salt: _, ...domain } = await (async () => {
if (parameters.verifierDomain) return parameters.verifierDomain
const { domain } = await getAction(
client,
getEip712Domain,
'getEip712Domain',
)({
address: verifier!,
factory,
factoryData,
})
return domain
})()
return getAction(
client,
signTypedData,
'signTypedData',
)({
account,
domain,
types: {
PersonalSign: [{ name: 'prefixed', type: 'bytes' }],
},
primaryType: 'PersonalSign',
message: {
prefixed: toPrefixedMessage(message),
},
})
}

View File

@@ -0,0 +1,237 @@
import type { Address, TypedData, TypedDataDomain } from 'abitype'
import type { Account } from '../../../accounts/types.js'
import { parseAccount } from '../../../accounts/utils/parseAccount.js'
import {
type GetEip712DomainParameters,
getEip712Domain,
} from '../../../actions/public/getEip712Domain.js'
import { signTypedData as signTypedData_ } from '../../../actions/wallet/signTypedData.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import { AccountNotFoundError } from '../../../errors/account.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { GetAccountParameter } from '../../../types/account.js'
import type { Chain } from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
import type { TypedDataDefinition } from '../../../types/typedData.js'
import type { OneOf, RequiredBy } from '../../../types/utils.js'
import { getAction } from '../../../utils/getAction.js'
import type { GetVerifierParameter } from '../types.js'
import { wrapTypedDataSignature } from '../utils/wrapTypedDataSignature.js'
export type SignTypedDataParameters<
typedData extends TypedData | Record<string, unknown> = TypedData,
primaryType extends keyof typedData | 'EIP712Domain' = keyof typedData,
account extends Account | undefined = undefined,
accountOverride extends Account | undefined = undefined,
verifier extends Address | undefined = Address | undefined,
///
primaryTypes = typedData extends TypedData ? keyof typedData : string,
> = TypedDataDefinition<typedData, primaryType, primaryTypes> &
Pick<GetEip712DomainParameters, 'factory' | 'factoryData'> &
GetAccountParameter<account, accountOverride> &
OneOf<
| {
verifierDomain: RequiredBy<
TypedDataDomain,
'chainId' | 'name' | 'verifyingContract' | 'salt' | 'version'
>
verifier?: undefined
}
| (GetVerifierParameter<verifier> & {
verifierDomain?:
| RequiredBy<
TypedDataDomain,
'chainId' | 'name' | 'verifyingContract' | 'salt' | 'version'
>
| undefined
})
>
export type SignTypedDataReturnType = Hex
export type SignTypedDataErrorType = ErrorType
/**
* Signs an [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data message via [ERC-7739 `TypedDataSign` format](https://eips.ethereum.org/EIPS/eip-7702).
*
* This Action is suitable to sign messages for Smart Accounts that implement (or conform to) [ERC-7739](https://eips.ethereum.org/EIPS/eip-7702) (e.g. Solady's [ERC1271.sol](https://github.com/Vectorized/solady/blob/main/src/accounts/ERC1271.sol)).
*
* - Docs: https://viem.sh/experimental/erc7739/signTypedData
*
* @param client - Client to use
* @param parameters - {@link SignTypedDataParameters}
* @returns The signed data. {@link SignTypedDataReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { signTypedData } from 'viem/experimental/erc7739'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
* const signature = await signTypedData(client, {
* account: '0xE8Df82fA4E10e6A12a9Dab552bceA2acd26De9bb',
* 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: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* })
*
* @example
* // Account Hoisting
* import { createWalletClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { signTypedData } from 'viem/experimental/erc7739'
*
* const client = createWalletClient({
* account: '0xE8Df82fA4E10e6A12a9Dab552bceA2acd26De9bb'
* chain: mainnet,
* transport: http(),
* })
* const signature = await signTypedData(client, {
* 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: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* })
*/
export async function signTypedData<
const typedData extends TypedData | Record<string, unknown>,
primaryType extends keyof typedData | 'EIP712Domain',
chain extends Chain | undefined,
account extends Account | undefined,
accountOverride extends Account | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: SignTypedDataParameters<
typedData,
primaryType,
account,
accountOverride
>,
): Promise<SignTypedDataReturnType> {
const {
account: account_ = client.account,
domain,
factory,
factoryData,
message,
primaryType,
types,
verifier,
} = parameters as unknown as SignTypedDataParameters
if (!account_)
throw new AccountNotFoundError({
docsPath: '/experimental/erc7739/signTypedData',
})
const account = parseAccount(account_!)
// Retrieve account EIP712 domain.
const { domain: verifierDomain } = await (async () => {
if (parameters.verifierDomain)
return {
domain: parameters.verifierDomain,
}
return getAction(
client,
getEip712Domain,
'getEip712Domain',
)({
address: verifier!,
factory,
factoryData,
})
})()
// Sign with typed data wrapper.
const signature = await getAction(
client,
signTypedData_,
'signTypedData',
)({
account,
domain,
types: {
...types,
TypedDataSign: [
{ name: 'contents', type: primaryType },
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
{ name: 'salt', type: 'bytes32' },
],
},
primaryType: 'TypedDataSign',
message: {
contents: message as any,
...(verifierDomain as any),
},
})
return wrapTypedDataSignature({
domain,
message,
primaryType,
signature,
types,
})
}

View File

@@ -0,0 +1,232 @@
import type { Address, TypedData } from 'abitype'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Account } from '../../../types/account.js'
import type { Chain } from '../../../types/chain.js'
import {
type SignMessageParameters,
type SignMessageReturnType,
signMessage,
} from '../actions/signMessage.js'
import {
type SignTypedDataParameters,
type SignTypedDataReturnType,
signTypedData,
} from '../actions/signTypedData.js'
export type Erc7739Actions<
account extends Account | undefined = Account | undefined,
verifier extends Address | undefined = Address | undefined,
> = {
/**
* Signs a [EIP-191](https://eips.ethereum.org/EIPS/eip-191) personal sign message via [ERC-7739 `PersonalSign` format](https://eips.ethereum.org/EIPS/eip-7702).
*
* This Action is suitable to sign messages for Smart Accounts that implement (or conform to) [ERC-7739](https://eips.ethereum.org/EIPS/eip-7702) (e.g. Solady's [ERC1271.sol](https://github.com/Vectorized/solady/blob/main/src/accounts/ERC1271.sol)).
*
* - Docs: https://viem.sh/experimental/erc7739/signMessage
*
* With the calculated signature, you can:
* - use [`verifyMessage`](https://viem.sh/docs/utilities/verifyMessage) to verify the signature,
*
* @param client - Client to use
* @param parameters - {@link SignMessageParameters}
* @returns The signed message. {@link SignMessageReturnType}
*
* @example
* ```ts
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7739Actions } from 'viem/experimental/erc7739'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(erc7739Actions())
*
* const signature = await client.signMessage({
* account: '0xE8Df82fA4E10e6A12a9Dab552bceA2acd26De9bb',
* message: 'hello world',
* verifier: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* })
* ```
*
* @example Account & Signer Hoisting
* ```ts
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7739Actions } from 'viem/experimental/erc7739'
*
* const client = createWalletClient({
* account: '0xE8Df82fA4E10e6A12a9Dab552bceA2acd26De9bb',
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(erc7739Actions({ verifier: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e' }))
*
* const signature = await client.signMessage({
* message: 'hello world',
* })
* ```
*/
signMessage: <accountOverride extends Account | undefined = undefined>(
parameters: SignMessageParameters<account, accountOverride, verifier>,
) => Promise<SignMessageReturnType>
/**
* Signs an [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data message via [ERC-7739 `TypedDataSign` format](https://eips.ethereum.org/EIPS/eip-7702).
*
* This Action is suitable to sign messages for Smart Accounts that implement (or conform to) [ERC-7739](https://eips.ethereum.org/EIPS/eip-7702) (e.g. Solady's [ERC1271.sol](https://github.com/Vectorized/solady/blob/main/src/accounts/ERC1271.sol)).
*
* - Docs: https://viem.sh/experimental/erc7739/signTypedData
*
* @param client - Client to use
* @param parameters - {@link SignTypedDataParameters}
* @returns The signed data. {@link SignTypedDataReturnType}
*
* @example
* ```ts
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7739Actions } from 'viem/experimental/erc7739'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(erc7739Actions())
*
* const signature = await client.signTypedData({
* account: '0xE8Df82fA4E10e6A12a9Dab552bceA2acd26De9bb',
* 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: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* })
* ```
*
* @example Account & Signer Hoisting
* ```ts
* import { createWalletClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { erc7739Actions } from 'viem/experimental/erc7739'
*
* const client = createWalletClient({
* account: '0xE8Df82fA4E10e6A12a9Dab552bceA2acd26De9bb',
* chain: mainnet,
* transport: http(),
* }).extend(erc7739Actions({ verifier: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e' }))
*
* const signature = await client.signTypedData({
* 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!',
* },
* })
* ```
*/
signTypedData: <
const typedData extends TypedData | Record<string, unknown>,
primaryType extends keyof typedData | 'EIP712Domain',
accountOverride extends Account | undefined = undefined,
>(
parameters: SignTypedDataParameters<
typedData,
primaryType,
account,
accountOverride
>,
) => Promise<SignTypedDataReturnType>
}
export type Erc7739ActionsParameters<
verifier extends Account | Address | undefined =
| Account
| Address
| undefined,
> = {
verifier?: verifier | undefined
}
/**
* A suite of Actions based on [Solady contracts](https://github.com/Vectorized/solady).
*
* @example
* import { createPublicClient, createWalletClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7739Actions } from 'viem/experimental'
*
* const walletClient = createWalletClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7739Actions())
*
* const result = await walletClient.signMessage({...})
*/
export function erc7739Actions<
verifier extends Address | undefined = undefined,
>(parameters: Erc7739ActionsParameters<verifier> = {}) {
const { verifier } = parameters
return <
transport extends Transport,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
>(
client: Client<transport, chain, account>,
): Erc7739Actions<account, verifier> => {
return {
signMessage: (parameters) =>
signMessage(client, { verifier, ...parameters }),
signTypedData: (parameters) =>
signTypedData(client, { verifier, ...(parameters as any) }),
}
}
}

36
node_modules/viem/experimental/erc7739/index.ts generated vendored Normal file
View File

@@ -0,0 +1,36 @@
// biome-ignore lint/performance/noBarrelFile: entrypoint
export {
type SignMessageErrorType,
type SignMessageParameters,
type SignMessageReturnType,
signMessage,
} from './actions/signMessage.js'
export {
type SignTypedDataErrorType,
type SignTypedDataParameters,
type SignTypedDataReturnType,
signTypedData,
} from './actions/signTypedData.js'
export {
type Erc7739Actions,
type Erc7739ActionsParameters,
erc7739Actions,
} from './decorators/erc7739.js'
export {
type HashMessageErrorType,
type HashMessageParameters,
type HashMessageReturnType,
hashMessage,
} from './utils/hashMessage.js'
export {
type HashTypedDataErrorType,
type HashTypedDataParameters,
type HashTypedDataReturnType,
hashTypedData,
} from './utils/hashTypedData.js'
export {
type WrapTypedDataSignatureErrorType,
type WrapTypedDataSignatureParameters,
type WrapTypedDataSignatureReturnType,
wrapTypedDataSignature,
} from './utils/wrapTypedDataSignature.js'

6
node_modules/viem/experimental/erc7739/package.json generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"type": "module",
"types": "../../_types/experimental/erc7739/index.d.ts",
"module": "../../_esm/experimental/erc7739/index.js",
"main": "../../_cjs/experimental/erc7739/index.js"
}

7
node_modules/viem/experimental/erc7739/types.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
import type { Address } from 'abitype'
export type GetVerifierParameter<
verifier extends Address | undefined = Address,
> = verifier extends Address
? { verifier?: Address | undefined }
: { verifier: Address }

View File

@@ -0,0 +1,60 @@
import type { TypedDataDomain } from 'abitype'
import type { ErrorType } from '../../../errors/utils.js'
import type { Hex, SignableMessage } from '../../../types/misc.js'
import type { RequiredBy } from '../../../types/utils.js'
import { hashTypedData } from '../../../utils/index.js'
import type { HashTypedDataErrorType } from '../../../utils/signature/hashTypedData.js'
import {
type ToPrefixedMessageErrorType,
toPrefixedMessage,
} from '../../../utils/signature/toPrefixedMessage.js'
export type HashMessageParameters = {
message: SignableMessage
verifierDomain: RequiredBy<
TypedDataDomain,
'chainId' | 'name' | 'verifyingContract' | 'version'
>
}
export type HashMessageReturnType = Hex
export type HashMessageErrorType =
| HashTypedDataErrorType
| ToPrefixedMessageErrorType
| ErrorType
/**
* Generates a signable hash for a ERC-7739 personal sign message.
*
* @example
* ```ts
* const hash = hashMessage({
* message: 'hello world',
* verifierDomain: {
* name: 'Smart Account',
* version: '1',
* verifyingContract: '0x1234567890abcdef1234567890abcdef12345678',
* chainId: 1,
* },
* })
* ```
*/
export function hashMessage(
parameters: HashMessageParameters,
): HashMessageReturnType {
const {
message,
verifierDomain: { salt: _, ...domain },
} = parameters
return hashTypedData({
domain,
types: {
PersonalSign: [{ name: 'prefixed', type: 'bytes' }],
},
primaryType: 'PersonalSign',
message: {
prefixed: toPrefixedMessage(message),
},
})
}

View File

@@ -0,0 +1,98 @@
import type { TypedData, TypedDataDomain } from 'abitype'
import type { ErrorType } from '../../../errors/utils.js'
import type { Hex } from '../../../types/misc.js'
import type { TypedDataDefinition } from '../../../types/typedData.js'
import type { RequiredBy } from '../../../types/utils.js'
import {
type HashTypedDataErrorType as HashTypedDataErrorType_,
hashTypedData as hashTypedData_,
} from '../../../utils/signature/hashTypedData.js'
export type HashTypedDataParameters<
typedData extends TypedData | Record<string, unknown> = TypedData,
primaryType extends keyof typedData | 'EIP712Domain' = keyof typedData,
///
primaryTypes = typedData extends TypedData ? keyof typedData : string,
> = TypedDataDefinition<typedData, primaryType, primaryTypes> & {
verifierDomain: RequiredBy<
TypedDataDomain,
'chainId' | 'name' | 'verifyingContract' | 'salt' | 'version'
>
}
export type HashTypedDataReturnType = Hex
export type HashTypedDataErrorType = HashTypedDataErrorType_ | ErrorType
/**
* Generates a signable hash for ERC-7739 typed data.
*
* @example
* ```ts
* const hash = hashTypedData({
* 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: 'Smart Account',
* version: '1',
* verifyingContract: '0x1234567890abcdef1234567890abcdef12345678',
* chainId: 1,
* },
* })
* ```
*/
export function hashTypedData<
const typedData extends TypedData | Record<string, unknown>,
primaryType extends keyof typedData | 'EIP712Domain',
>(
parameters: HashTypedDataParameters<typedData, primaryType>,
): HashTypedDataReturnType {
const { domain, message, primaryType, types, verifierDomain } = parameters
return hashTypedData_({
domain: domain as any,
types: {
...types,
TypedDataSign: [
{ name: 'contents', type: primaryType },
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
{ name: 'salt', type: 'bytes32' },
],
},
primaryType: 'TypedDataSign',
message: {
contents: message as any,
...(verifierDomain as any),
},
})
}

View File

@@ -0,0 +1,108 @@
import type { TypedData } from 'abitype'
import { serializeSignature } from '../../../accounts/index.js'
import type { ByteArray, Hex, Signature } from '../../../types/misc.js'
import type { TypedDataDefinition } from '../../../types/typedData.js'
import { encodePacked } from '../../../utils/abi/encodePacked.js'
import { type IsHexErrorType, isHex } from '../../../utils/data/isHex.js'
import { size } from '../../../utils/data/size.js'
import { bytesToHex, stringToHex } from '../../../utils/encoding/toHex.js'
import {
encodeType,
hashStruct,
} from '../../../utils/signature/hashTypedData.js'
import { getTypesForEIP712Domain } from '../../../utils/typedData.js'
export type WrapTypedDataSignatureParameters<
typedData extends TypedData | Record<string, unknown> = TypedData,
primaryType extends keyof typedData | 'EIP712Domain' = keyof typedData,
///
primaryTypes = typedData extends TypedData ? keyof typedData : string,
> = TypedDataDefinition<typedData, primaryType, primaryTypes> & {
signature: Hex | ByteArray | Signature
}
export type WrapTypedDataSignatureReturnType = Hex
export type WrapTypedDataSignatureErrorType = IsHexErrorType
/**
* Wraps a typed data signature for ERC-7739.
*
* @example
* ```ts
* const signature = wrapTypedDataSignature({
* 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!',
* },
* signature: '0x...',
* })
* ```
*/
export function wrapTypedDataSignature(
parameters: WrapTypedDataSignatureParameters,
): WrapTypedDataSignatureReturnType {
const { domain, message, primaryType, signature, types } = parameters
const signatureHex = (() => {
if (isHex(signature)) return signature
if (typeof signature === 'object' && 'r' in signature && 's' in signature)
return serializeSignature(signature)
return bytesToHex(signature)
})()
// Compute dependencies for wrapped signature.
const hashedDomain = hashStruct({
data: domain ?? {},
types: {
EIP712Domain: getTypesForEIP712Domain({ domain }),
},
primaryType: 'EIP712Domain',
})
const hashedContents = hashStruct({
data: message,
types: types as any,
primaryType,
})
const encodedType = encodeType({
primaryType,
types: types as any,
})
// Construct wrapped signature.
return encodePacked(
['bytes', 'bytes32', 'bytes32', 'bytes', 'uint16'],
[
signatureHex,
hashedDomain,
hashedContents,
stringToHex(encodedType),
size(stringToHex(encodedType)),
],
)
}

View File

@@ -0,0 +1,253 @@
import type { Address } from 'abitype'
import {
type ParseAccountErrorType,
parseAccount,
} from '../../../accounts/utils/parseAccount.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import { ethAddress } from '../../../constants/address.js'
import { AccountNotFoundError } from '../../../errors/account.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Account, GetAccountParameter } from '../../../types/account.js'
import type { Chain } from '../../../types/chain.js'
import type {
WalletGetAssetsParameters,
WalletGetAssetsReturnType,
} from '../../../types/eip1193.js'
import type { Hex } from '../../../types/misc.js'
import type { OneOf, Prettify } from '../../../types/utils.js'
import {
type HexToBigIntErrorType,
hexToBigInt,
} from '../../../utils/encoding/fromHex.js'
import {
type NumberToHexErrorType,
numberToHex,
} from '../../../utils/encoding/toHex.js'
export type GetAssetsParameters<
aggregate extends
| boolean
| ((asset: getAssets.Asset) => string)
| undefined = undefined,
account extends Account | undefined = Account | undefined,
> = GetAccountParameter<account> & {
/**
* Whether or not to aggregate assets across multiple chains,
* and assign them to a '0' key.
* @default true
*/
aggregate?:
| aggregate
| boolean
| ((asset: getAssets.Asset) => string)
| undefined
/** Filter by assets. */
assets?:
| {
[chainId: number]: readonly (
| {
address: 'native'
type: 'native'
}
| {
address: Address
type: getAssets.AssetType
}
)[]
}
| undefined
/** Filter by asset types. */
assetTypes?: readonly getAssets.AssetType[] | undefined
/** Filter by chain IDs. */
chainIds?: readonly number[] | undefined
}
export type GetAssetsReturnType<
aggregate extends
| boolean
| ((asset: getAssets.Asset) => string)
| undefined = undefined,
> = {
[chainId: number]: readonly getAssets.Asset<false>[]
} & (aggregate extends false ? {} : { 0: readonly getAssets.Asset<true>[] })
export type GetAssetsErrorType =
| HexToBigIntErrorType
| NumberToHexErrorType
| ParseAccountErrorType
| ErrorType
/**
* Retrieves assets for a given account from the target Wallet.
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { getAssets } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
*
* const result = await getAssets(client, {
* account: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
* })
*
* @param client - Client to use to make the request.
* @param parameters - Parameters.
* @returns Assets for the given account.
*/
export async function getAssets<
chain extends Chain | undefined,
account extends Account | undefined = Account | undefined,
aggregate extends
| boolean
| ((asset: getAssets.Asset) => string)
| undefined = undefined,
>(
client: Client<Transport, chain, account>,
...[parameters]: account extends Account
? [GetAssetsParameters<aggregate, account>] | []
: [GetAssetsParameters<aggregate, account>]
): Promise<Prettify<GetAssetsReturnType<aggregate>>> {
const { account = client.account, aggregate = true } = parameters ?? {}
const result = await client.request({
method: 'wallet_getAssets',
params: [formatRequest({ ...parameters, account })],
})
const response = formatResponse(result)
const aggregated = (() => {
if (!aggregate) return undefined
const aggregated = {} as Record<string, getAssets.Asset<boolean>>
for (const [chainId, assets] of Object.entries(response)) {
if (chainId === '0') continue
const seen = new Set<string>()
for (const asset of assets) {
const key =
typeof aggregate === 'function'
? aggregate(asset)
: (asset.address ?? ethAddress)
const item = (aggregated[key] ?? {}) as getAssets.Asset<true>
if (seen.has(key)) continue
seen.add(key)
aggregated[key] = {
...asset,
balance: asset.balance + (item?.balance ?? 0n),
chainIds: [...(item?.chainIds ?? []), Number(chainId)],
}
}
}
return Object.values(aggregated)
})()
if (aggregated) return { 0: aggregated, ...response } as never
return response as never
}
export declare namespace getAssets {
type Asset<chainIds extends boolean = false> = OneOf<
CustomAsset | Erc20Asset | Erc721Asset | NativeAsset
> &
(chainIds extends true ? { chainIds: readonly number[] } : {})
type AssetType = 'native' | 'erc20' | 'erc721' | (string & {})
type CustomAsset = {
address: Address
balance: bigint
metadata: {
[key: string]: unknown
}
type: { custom: string }
}
type Erc20Asset = {
address: Address
balance: bigint
metadata: {
name: string
symbol: string
decimals: number
}
type: 'erc20'
}
type Erc721Asset = {
address: Address
balance: bigint
metadata: {
name: string
symbol: string
tokenId: bigint
tokenUri?: string | undefined
}
type: 'erc721'
}
type NativeAsset = {
balance: bigint
type: 'native'
}
}
/** @internal */
function formatRequest(
parameters: GetAssetsParameters<undefined, Account> | undefined = {},
): WalletGetAssetsParameters {
const { account: account_, assets, assetTypes, chainIds } = parameters
if (typeof account_ === 'undefined')
throw new AccountNotFoundError({
docsPath: '/experimental/erc7811/getAssets',
})
const account = parseAccount(account_)
return {
account: account.address,
assetFilter: assets,
assetTypeFilter: assetTypes,
chainFilter: chainIds?.map((chainId) => numberToHex(chainId)),
}
}
/** @internal */
function formatResponse(
response: WalletGetAssetsReturnType,
): GetAssetsReturnType<false> {
return Object.fromEntries(
Object.entries(response).map(([chainId, assets]) => [
Number(chainId),
assets.map((asset) => {
const balance = hexToBigInt(asset.balance)
const metadata = asset.metadata as getAssets.Asset['metadata']
const type = (() => {
if (asset.type === 'native') return 'native'
if (asset.type === 'erc20') return 'erc20'
if (asset.type === 'erc721') return 'erc721'
return { custom: asset.type }
})()
const address = type === 'native' ? undefined : asset.address
return {
balance,
type,
...(address ? { address } : {}),
...(metadata
? {
metadata: {
...metadata,
...('tokenId' in metadata
? { tokenId: hexToBigInt(metadata.tokenId as Hex) }
: {}),
},
}
: {}),
}
}),
]),
) as GetAssetsReturnType<false>
}

View File

@@ -0,0 +1,79 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Account } from '../../../types/account.js'
import type { Chain } from '../../../types/chain.js'
import {
type GetAssetsParameters,
type GetAssetsReturnType,
getAssets,
} from '../actions/getAssets.js'
export type Erc7811Actions<
account extends Account | undefined = Account | undefined,
> = {
/**
* Requests to get assets for an account from a wallet.
*
* - Docs: https://viem.sh/experimental/erc7811/getAssets
*
* @param client - Client to use
* @param parameters - {@link GetAssetsParameters}
* @returns List of assets for the given account {@link GetAssetsReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7811Actions } from 'viem/experimental/erc7811'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(erc7811Actions())
*
* const response = await client.getAssets({
* account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
* })
*/
getAssets: <
aggregate extends
| boolean
| ((asset: getAssets.Asset) => string)
| undefined = undefined,
>(
...[parameters]: account extends Account
? [GetAssetsParameters<aggregate, account>] | []
: [GetAssetsParameters<aggregate, account>]
) => Promise<GetAssetsReturnType<aggregate>>
}
/**
* A suite of ERC-7811 Wallet Actions.
*
* @example
* import { createPublicClient, createWalletClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7811Actions } from 'viem/experimental/erc7811'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7811Actions())
*
* const response = await client.getAssets({
* account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
* })
*/
export function erc7811Actions() {
return <
transport extends Transport,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
>(
client: Client<transport, chain, account>,
): Erc7811Actions<account> => {
return {
// @ts-expect-error
getAssets: (...[parameters]) => getAssets(client, parameters),
}
}
}

9
node_modules/viem/experimental/erc7811/index.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// biome-ignore lint/performance/noBarrelFile: entrypoint
export {
type GetAssetsErrorType,
type GetAssetsParameters,
type GetAssetsReturnType,
getAssets,
} from './actions/getAssets.js'
export { type Erc7811Actions, erc7811Actions } from './decorators/erc7811.js'

6
node_modules/viem/experimental/erc7811/package.json generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"type": "module",
"types": "../../_types/experimental/erc7811/index.d.ts",
"module": "../../_esm/experimental/erc7811/index.js",
"main": "../../_cjs/experimental/erc7811/index.js"
}

View File

@@ -0,0 +1,171 @@
import type { Address, Narrow } from 'abitype'
import {
type SendTransactionErrorType,
sendTransaction,
} from '../../../actions/wallet/sendTransaction.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { BaseError } from '../../../errors/base.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Account, GetAccountParameter } from '../../../types/account.js'
import type { Calls } from '../../../types/calls.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
import type { UnionEvaluate, UnionPick } from '../../../types/utils.js'
import type { FormattedTransactionRequest } from '../../../utils/formatters/transactionRequest.js'
import { withCache } from '../../../utils/promise/withCache.js'
import { executionMode } from '../constants.js'
import { ExecuteUnsupportedError } from '../errors.js'
import {
type EncodeExecuteDataErrorType,
encodeExecuteData,
} from '../utils/encodeExecuteData.js'
import {
type GetExecuteErrorReturnType,
getExecuteError,
} from '../utils/getExecuteError.js'
import { supportsExecutionMode } from './supportsExecutionMode.js'
export type ExecuteParameters<
calls extends readonly unknown[] = readonly unknown[],
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionPick<
FormattedTransactionRequest<_derivedChain>,
| 'authorizationList'
| 'gas'
| 'gasPrice'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
>
> &
GetAccountParameter<account, Account | Address, true, true> &
GetChainParameter<chain, chainOverride> & {
/** Address that will execute the calls. */
address: Address
/** Calls to execute. */
calls: Calls<Narrow<calls>>
/** Additional data to include for execution. */
opData?: Hex | undefined
}
export type ExecuteReturnType = Hex
export type ExecuteErrorType =
| GetExecuteErrorReturnType
| EncodeExecuteDataErrorType
| SendTransactionErrorType
| ErrorType
/**
* Executes call(s) using the `execute` function on an [ERC-7821-compatible contract](https://eips.ethereum.org/EIPS/eip-7821).
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { execute } from 'viem/experimental/erc7821'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await execute(client, {
* account,
* calls: [{
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 69420n,
* },
* }],
* to: account.address,
* })
* ```
*
* @example
* ```ts
* // Account Hoisting
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { execute } from 'viem/experimental/erc7821'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* account,
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await execute(client, {
* calls: [{
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 69420n,
* },
* }],
* to: account.address,
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link ExecuteParameters}
* @returns Transaction hash. {@link ExecuteReturnType}
*/
export async function execute<
const calls extends readonly unknown[],
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: ExecuteParameters<calls, chain, account, chainOverride>,
): Promise<ExecuteReturnType> {
const { authorizationList, calls, opData } = parameters
const address = authorizationList?.[0]?.address ?? parameters.address
const mode = opData ? executionMode.opData : executionMode.default
const supported = await withCache(
() =>
supportsExecutionMode(client, {
address,
mode,
}),
{
cacheKey: `supportsExecutionMode.${client.uid}.${address}.${mode}`,
},
)
if (!supported) throw new ExecuteUnsupportedError()
try {
return await sendTransaction(client, {
...parameters,
to: parameters.address,
data: encodeExecuteData({ calls, opData }),
} as any)
} catch (e) {
throw getExecuteError(e as BaseError, { calls })
}
}

View File

@@ -0,0 +1,194 @@
import type { Address, Narrow } from 'abitype'
import {
type SendTransactionErrorType,
sendTransaction,
} from '../../../actions/wallet/sendTransaction.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { BaseError } from '../../../errors/base.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Account, GetAccountParameter } from '../../../types/account.js'
import type { Batches, Calls } from '../../../types/calls.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
import type { UnionEvaluate, UnionPick } from '../../../types/utils.js'
import type { FormattedTransactionRequest } from '../../../utils/formatters/transactionRequest.js'
import { withCache } from '../../../utils/promise/withCache.js'
import { ExecuteUnsupportedError } from '../errors.js'
import {
type EncodeExecuteBatchesDataErrorType,
encodeExecuteBatchesData,
} from '../utils/encodeExecuteBatchesData.js'
import {
type GetExecuteErrorReturnType,
getExecuteError,
} from '../utils/getExecuteError.js'
import { supportsExecutionMode } from './supportsExecutionMode.js'
/** @internal */
export type Batch = { calls: readonly unknown[]; opData?: Hex | undefined }
export type ExecuteBatchesParameters<
batches extends readonly Batch[] = readonly Batch[],
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionPick<
FormattedTransactionRequest<_derivedChain>,
| 'authorizationList'
| 'gas'
| 'gasPrice'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
>
> &
GetAccountParameter<account, Account | Address, true, true> &
GetChainParameter<chain, chainOverride> & {
/** Address that will execute the calls. */
address: Address
/** Batches to execute. */
batches: Batches<Narrow<batches>, { opData?: Hex | undefined }>
}
export type ExecuteBatchesReturnType = Hex
export type ExecuteBatchesErrorType =
| GetExecuteErrorReturnType
| EncodeExecuteBatchesDataErrorType
| SendTransactionErrorType
| ErrorType
/**
* Executes batches of call(s) using "batch of batches" mode on an [ERC-7821-compatible contract](https://eips.ethereum.org/EIPS/eip-7821).
*
* @example
* ```ts
* import { createClient, http, parseEther } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { executeBatches } from 'viem/experimental/erc7821'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await executeBatches(client, {
* account,
* batches: [
* {
* calls: [
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* ],
* },
* {
* calls: [
* {
* to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
* value: parseEther('2'),
* },
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* ],
* },
* ],
* to: account.address,
* })
* ```
*
* @example
* ```ts
* // Account Hoisting
* import { createClient, http, parseEther } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { executeBatches } from 'viem/experimental/erc7821'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await executeBatches(client, {
* batches: [
* {
* calls: [
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* ],
* },
* {
* calls: [
* {
* to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
* value: parseEther('2'),
* },
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* ],
* },
* ],
* to: account.address,
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link ExecuteBatchesParameters}
* @returns Transaction hash. {@link ExecuteBatchesReturnType}
*/
export async function executeBatches<
batches extends readonly Batch[],
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: ExecuteBatchesParameters<batches, chain, account, chainOverride>,
): Promise<ExecuteBatchesReturnType> {
const { authorizationList, batches } = parameters
const address = authorizationList?.[0]?.address ?? parameters.address
const supported = await withCache(
() =>
supportsExecutionMode(client, {
address,
mode: 'batchOfBatches',
}),
{
cacheKey: `supportsExecutionMode.${client.uid}.${address}.batchOfBatches`,
},
)
if (!supported) throw new ExecuteUnsupportedError()
try {
return await sendTransaction(client, {
...parameters,
to: parameters.address,
data: encodeExecuteBatchesData({ batches }),
} as any)
} catch (e) {
const calls = batches.flatMap((b) => b.calls) as Calls<Narrow<batches>>
throw getExecuteError(e as BaseError, { calls })
}
}

View File

@@ -0,0 +1,73 @@
import type { Address } from '../../../accounts/index.js'
import { readContract } from '../../../actions/public/readContract.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Chain } from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
import { withCache } from '../../../utils/promise/withCache.js'
import { abi, executionMode } from '../constants.js'
export type SupportsExecutionModeParameters = {
address: Address
mode?: 'default' | 'opData' | 'batchOfBatches' | Hex
}
export type SupportsExecutionModeReturnType = boolean
export type SupportsExecutionModeErrorType = ErrorType
const toSerializedMode = {
default: executionMode.default,
opData: executionMode.opData,
batchOfBatches: executionMode.batchOfBatches,
} as const
/**
* Checks if the contract supports the ERC-7821 execution mode.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { supportsExecutionMode } from 'viem/experimental/erc7821'
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* })
*
* const supported = await supportsExecutionMode(client, {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link SupportsExecutionModeParameters}
* @returns If the execution mode is supported. {@link SupportsExecutionModeReturnType}
*/
export async function supportsExecutionMode<
chain extends Chain | undefined = Chain | undefined,
>(
client: Client<Transport, chain>,
parameters: SupportsExecutionModeParameters,
): Promise<SupportsExecutionModeReturnType> {
const { address, mode: m = 'default' } = parameters
const mode = m.startsWith('0x') ? m : (toSerializedMode as any)[m]
try {
return await withCache(
() =>
readContract(client, {
abi,
address,
functionName: 'supportsExecutionMode',
args: [mode],
}),
{
cacheKey: `supportsExecutionMode.${address}.${mode}`,
},
)
} catch {
return false
}
}

64
node_modules/viem/experimental/erc7821/constants.ts generated vendored Normal file
View File

@@ -0,0 +1,64 @@
export const abi = [
{
type: 'fallback',
stateMutability: 'payable',
},
{
type: 'receive',
stateMutability: 'payable',
},
{
type: 'function',
name: 'execute',
inputs: [
{
name: 'mode',
type: 'bytes32',
internalType: 'bytes32',
},
{
name: 'executionData',
type: 'bytes',
internalType: 'bytes',
},
],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'supportsExecutionMode',
inputs: [
{
name: 'mode',
type: 'bytes32',
internalType: 'bytes32',
},
],
outputs: [
{
name: 'result',
type: 'bool',
internalType: 'bool',
},
],
stateMutability: 'view',
},
{
type: 'error',
name: 'FnSelectorNotRecognized',
inputs: [],
},
{
type: 'error',
name: 'UnsupportedExecutionMode',
inputs: [],
},
] as const
export const executionMode = {
default: '0x0100000000000000000000000000000000000000000000000000000000000000',
opData: '0x0100000000007821000100000000000000000000000000000000000000000000',
batchOfBatches:
'0x0100000000007821000200000000000000000000000000000000000000000000',
} as const

View File

@@ -0,0 +1,258 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Account } from '../../../types/account.js'
import type { Chain } from '../../../types/chain.js'
import {
type ExecuteParameters,
type ExecuteReturnType,
execute,
} from '../actions/execute.js'
import {
type Batch,
type ExecuteBatchesParameters,
type ExecuteBatchesReturnType,
executeBatches,
} from '../actions/executeBatches.js'
import {
type SupportsExecutionModeParameters,
type SupportsExecutionModeReturnType,
supportsExecutionMode,
} from '../actions/supportsExecutionMode.js'
export type Erc7821Actions<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
> = {
/**
* Executes call(s) using the `execute` function on an [ERC-7821-compatible contract](https://eips.ethereum.org/EIPS/eip-7821).
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const hash = await client.execute({
* account,
* calls: [{
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 69420n,
* },
* }],
* to: account.address,
* })
* ```
*
* @example
* ```ts
* // Account Hoisting
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* account,
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const hash = await client.execute({
* calls: [{
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 69420n,
* },
* }],
* to: account.address,
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link ExecuteParameters}
* @returns Transaction hash. {@link ExecuteReturnType}
*/
execute: <
const calls extends readonly unknown[],
chainOverride extends Chain | undefined = undefined,
>(
parameters: ExecuteParameters<calls, chain, account, chainOverride>,
) => Promise<ExecuteReturnType>
/**
* Executes batches of call(s) using "batch of batches" mode on an [ERC-7821-compatible contract](https://eips.ethereum.org/EIPS/eip-7821).
*
* @example
* ```ts
* import { createClient, http, parseEther } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const hash = await client.executeBatches({
* account,
* batches: [
* {
* calls: [
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* ],
* },
* {
* calls: [
* {
* to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
* value: parseEther('2'),
* },
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* ],
* },
* ],
* to: account.address,
* })
* ```
*
* @example
* ```ts
* // Account Hoisting
* import { createClient, http, parseEther } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const hash = await client.executeBatches({
* batches: [
* {
* calls: [
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* ],
* },
* {
* calls: [
* {
* to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
* value: parseEther('2'),
* },
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* ],
* },
* ],
* to: account.address,
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link ExecuteBatchesParameters}
* @returns Transaction hash. {@link ExecuteBatchesReturnType}
*/
executeBatches: <
const batches extends readonly Batch[],
chainOverride extends Chain | undefined = undefined,
>(
parameters: ExecuteBatchesParameters<
batches,
chain,
account,
chainOverride
>,
) => Promise<ExecuteBatchesReturnType>
/**
* Checks if the contract supports the ERC-7821 execution mode.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const supported = await supportsExecutionMode(client, {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link SupportsExecutionModeParameters}
* @returns If the execution mode is supported. {@link SupportsExecutionModeReturnType}
*/
supportsExecutionMode: (
parameters: SupportsExecutionModeParameters,
) => Promise<SupportsExecutionModeReturnType>
}
/**
* A suite of Actions for [ERC-7821](https://eips.ethereum.org/EIPS/eip-7821).
*
* @example
* import { createClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*/
export function erc7821Actions() {
return <
transport extends Transport,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
>(
client: Client<transport, chain, account>,
): Erc7821Actions<chain, account> => {
return {
execute: (parameters) => execute(client, parameters),
executeBatches: (parameters) => executeBatches(client, parameters),
supportsExecutionMode: (parameters) =>
supportsExecutionMode(client, parameters),
}
}
}

29
node_modules/viem/experimental/erc7821/errors.ts generated vendored Normal file
View File

@@ -0,0 +1,29 @@
import { BaseError } from '../../errors/base.js'
export type ExecuteUnsupportedErrorType = ExecuteUnsupportedError & {
name: 'ExecuteUnsupportedError'
}
export class ExecuteUnsupportedError extends BaseError {
constructor() {
super('ERC-7821 execution is not supported.', {
name: 'ExecuteUnsupportedError',
})
}
}
export type FunctionSelectorNotRecognizedErrorType =
FunctionSelectorNotRecognizedError & {
name: 'FunctionSelectorNotRecognizedError'
}
export class FunctionSelectorNotRecognizedError extends BaseError {
constructor() {
super('Function is not recognized.', {
metaMessages: [
'This could be due to any of the following:',
' - The contract does not have the function,',
' - The address is not a contract.',
],
name: 'FunctionSelectorNotRecognizedError',
})
}
}

48
node_modules/viem/experimental/erc7821/index.ts generated vendored Normal file
View File

@@ -0,0 +1,48 @@
// biome-ignore lint/performance/noBarrelFile: entrypoint
export {
type ExecuteErrorType,
type ExecuteParameters,
type ExecuteReturnType,
execute,
} from './actions/execute.js'
export {
type ExecuteBatchesErrorType,
type ExecuteBatchesParameters,
type ExecuteBatchesReturnType,
executeBatches,
} from './actions/executeBatches.js'
export {
type SupportsExecutionModeErrorType,
type SupportsExecutionModeParameters,
type SupportsExecutionModeReturnType,
supportsExecutionMode,
} from './actions/supportsExecutionMode.js'
export { type Erc7821Actions, erc7821Actions } from './decorators/erc7821.js'
export {
ExecuteUnsupportedError,
type ExecuteUnsupportedErrorType,
FunctionSelectorNotRecognizedError,
type FunctionSelectorNotRecognizedErrorType,
} from './errors.js'
export {
type EncodeCallsErrorType,
encodeCalls,
} from './utils/encodeCalls.js'
export {
type EncodeExecuteBatchesDataErrorType,
type EncodeExecuteBatchesDataParameters,
type EncodeExecuteBatchesDataReturnType,
encodeExecuteBatchesData,
} from './utils/encodeExecuteBatchesData.js'
export {
type EncodeExecuteDataErrorType,
type EncodeExecuteDataParameters,
type EncodeExecuteDataReturnType,
encodeExecuteData,
} from './utils/encodeExecuteData.js'
export {
type GetExecuteErrorParameters,
type GetExecuteErrorReturnType,
getExecuteError,
} from './utils/getExecuteError.js'

6
node_modules/viem/experimental/erc7821/package.json generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"type": "module",
"types": "../../_types/experimental/erc7821/index.d.ts",
"module": "../../_esm/experimental/erc7821/index.js",
"main": "../../_cjs/experimental/erc7821/index.js"
}

View File

@@ -0,0 +1,38 @@
import * as AbiParameters from 'ox/AbiParameters'
import type { ErrorType } from '../../../errors/utils.js'
import type { Call, Calls } from '../../../types/calls.js'
import type { Hex } from '../../../types/misc.js'
import {
type EncodeFunctionDataErrorType,
encodeFunctionData,
} from '../../../utils/abi/encodeFunctionData.js'
export type EncodeCallsErrorType =
| AbiParameters.encode.ErrorType
| AbiParameters.from.ErrorType
| EncodeFunctionDataErrorType
| ErrorType
export function encodeCalls(
calls_: Calls<readonly unknown[]>,
opData?: Hex | undefined,
) {
const calls = calls_.map((call_) => {
const call = call_ as Call
return {
data: call.abi ? encodeFunctionData(call) : (call.data ?? '0x'),
value: call.value ?? 0n,
target: call.to,
}
})
return AbiParameters.encode(
AbiParameters.from([
'struct Call { address target; uint256 value; bytes data; }',
'Call[] calls',
...(opData ? ['bytes opData'] : []),
]),
[calls, ...(opData ? [opData] : [])] as any,
)
}

View File

@@ -0,0 +1,48 @@
import type { Narrow } from 'abitype'
import * as AbiParameters from 'ox/AbiParameters'
import type { ErrorType } from '../../../errors/utils.js'
import type { Batches } from '../../../types/calls.js'
import type { Hex } from '../../../types/misc.js'
import {
type EncodeFunctionDataErrorType,
encodeFunctionData,
} from '../../../utils/abi/encodeFunctionData.js'
import { abi, executionMode } from '../constants.js'
import { type EncodeCallsErrorType, encodeCalls } from './encodeCalls.js'
/** @internal */
export type Batch = { calls: readonly unknown[]; opData?: Hex | undefined }
export type EncodeExecuteBatchesDataParameters<
batches extends readonly Batch[] = readonly Batch[],
> = {
/** Batches to execute. */
batches: Batches<Narrow<batches>, { opData?: Hex | undefined }>
}
export type EncodeExecuteBatchesDataReturnType = Hex
export type EncodeExecuteBatchesDataErrorType =
| EncodeCallsErrorType
| EncodeFunctionDataErrorType
| ErrorType
export function encodeExecuteBatchesData<batches extends readonly Batch[]>(
parameters: EncodeExecuteBatchesDataParameters<batches>,
): EncodeExecuteBatchesDataReturnType {
const { batches } = parameters
const encodedBatches = AbiParameters.encode(AbiParameters.from('bytes[]'), [
batches.map((b) => {
const batch = b as Batch
return encodeCalls(batch.calls, batch.opData)
}),
])
return encodeFunctionData({
abi,
functionName: 'execute',
args: [executionMode.batchOfBatches, encodedBatches],
})
}

View File

@@ -0,0 +1,42 @@
import type { Narrow } from 'abitype'
import type { ErrorType } from '../../../errors/utils.js'
import type { Calls } from '../../../types/calls.js'
import type { Hex } from '../../../types/misc.js'
import {
type EncodeFunctionDataErrorType,
encodeFunctionData,
} from '../../../utils/abi/encodeFunctionData.js'
import { abi, executionMode } from '../constants.js'
import { type EncodeCallsErrorType, encodeCalls } from './encodeCalls.js'
export type EncodeExecuteDataParameters<
calls extends readonly unknown[] = readonly unknown[],
> = {
/** Calls to execute. */
calls: Calls<Narrow<calls>>
/** Additional data to include for execution. */
opData?: Hex | undefined
}
export type EncodeExecuteDataReturnType = Hex
export type EncodeExecuteDataErrorType =
| EncodeCallsErrorType
| EncodeFunctionDataErrorType
| ErrorType
export function encodeExecuteData<const calls extends readonly unknown[]>(
parameters: EncodeExecuteDataParameters<calls>,
): EncodeExecuteDataReturnType {
const { calls, opData } = parameters
const encodedCalls = encodeCalls(calls, opData)
const mode = opData ? executionMode.opData : executionMode.default
return encodeFunctionData({
abi,
functionName: 'execute',
args: [mode, encodedCalls],
})
}

View File

@@ -0,0 +1,67 @@
import type { Abi, Narrow } from 'abitype'
import * as AbiError from 'ox/AbiError'
import type { BaseError } from '../../../errors/base.js'
import type { Call, Calls } from '../../../types/calls.js'
import type { Hex } from '../../../types/misc.js'
import { decodeErrorResult } from '../../../utils/abi/decodeErrorResult.js'
import {
type GetContractErrorReturnType,
getContractError,
} from '../../../utils/errors/getContractError.js'
import {
FunctionSelectorNotRecognizedError,
type FunctionSelectorNotRecognizedErrorType,
} from '../errors.js'
export type GetExecuteErrorParameters<
calls extends readonly unknown[] = readonly unknown[],
> = {
/** Calls to execute. */
calls: Calls<Narrow<calls>>
}
export type GetExecuteErrorReturnType =
| FunctionSelectorNotRecognizedErrorType
| GetContractErrorReturnType
export function getExecuteError<const calls extends readonly unknown[]>(
e: BaseError,
parameters: GetExecuteErrorParameters<calls>,
): GetExecuteErrorReturnType {
const error = e.walk((e) => 'data' in (e as Error)) as
| (BaseError & { data?: Hex | undefined })
| undefined
if (!error?.data) return e as never
if (
error.data ===
AbiError.getSelector(AbiError.from('error FnSelectorNotRecognized()'))
)
return new FunctionSelectorNotRecognizedError() as never
let matched: Call | null = null
for (const c of parameters.calls) {
const call = c as Call
if (!call.abi) continue
try {
const matches = Boolean(
decodeErrorResult({
abi: call.abi,
data: error.data!,
}),
)
if (!matches) continue
matched = call
} catch {}
}
if (matched)
return getContractError(error as BaseError, {
abi: matched.abi as Abi,
address: matched.to,
args: matched.args,
functionName: matched.functionName,
})
return e as never
}

View File

@@ -0,0 +1,179 @@
import type { Address } from 'abitype'
import {
type RequestAddressesErrorType,
requestAddresses,
} from '../../../actions/wallet/requestAddresses.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { BaseError } from '../../../errors/base.js'
import type { ExtractCapabilities } from '../../../types/capabilities.js'
import type { Chain } from '../../../types/chain.js'
import type { Prettify } from '../../../types/utils.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
import { numberToHex } from '../../../utils/encoding/toHex.js'
export type ConnectParameters = Prettify<{
capabilities?: ExtractCapabilities<'connect', 'Request'> | undefined
}>
export type ConnectReturnType = Prettify<{
accounts: readonly {
address: Address
capabilities?: ExtractCapabilities<'connect', 'ReturnType'> | undefined
}[]
}>
export type ConnectErrorType = RequestErrorType | RequestAddressesErrorType
/**
* Requests to connect account(s) with optional capabilities.
*
* - Docs: https://viem.sh/experimental/erc7846/connect
* - JSON-RPC Methods: [`wallet_connect`](https://github.com/ethereum/ERCs/blob/abd1c9f4eda2d6ad06ade0e3af314637a27d1ee7/ERCS/erc-7846.md)
*
* @param client - Client to use
* @param parameters - {@link ConnectParameters}
* @returns List of accounts managed by a wallet {@link ConnectReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { connect } from 'viem/experimental/erc7846'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
* const response = await connect(client)
*/
export async function connect<chain extends Chain | undefined>(
client: Client<Transport, chain>,
parameters: ConnectParameters = {},
): Promise<ConnectReturnType> {
const capabilities = formatRequestCapabilities(parameters.capabilities)
const response = await (async () => {
try {
return await client.request(
{ method: 'wallet_connect', params: [{ capabilities, version: '1' }] },
{ dedupe: true, retryCount: 0 },
)
} catch (e) {
const error = e as BaseError
// If the wallet does not support `wallet_connect`, and has no
// capabilities, attempt to use `eth_requestAccounts` instead.
if (
!parameters.capabilities &&
(error.name === 'InvalidInputRpcError' ||
error.name === 'InvalidParamsRpcError' ||
error.name === 'MethodNotFoundRpcError' ||
error.name === 'MethodNotSupportedRpcError' ||
error.name === 'UnsupportedProviderMethodError')
) {
const addresses = await requestAddresses(client)
return {
accounts: addresses.map((address) => ({
address,
capabilities: {},
})),
}
}
throw error
}
})()
return {
...response,
accounts: (response.accounts ?? []).map((account) => ({
...account,
capabilities: formatResponseCapabilities(account.capabilities),
})),
}
}
function formatRequestCapabilities(
capabilities: ExtractCapabilities<'connect', 'Request'> | undefined,
) {
const {
unstable_addSubAccount,
unstable_getSubAccounts: getSubAccounts,
unstable_signInWithEthereum,
...rest
} = capabilities ?? {}
const addSubAccount = unstable_addSubAccount
? {
...unstable_addSubAccount,
account: {
...unstable_addSubAccount.account,
...(unstable_addSubAccount.account.chainId
? {
chainId: numberToHex(unstable_addSubAccount.account.chainId),
}
: {}),
},
}
: undefined
const { chainId, expirationTime, issuedAt, notBefore } =
unstable_signInWithEthereum ?? {}
const signInWithEthereum = unstable_signInWithEthereum
? {
...unstable_signInWithEthereum,
chainId: numberToHex(chainId!),
...(expirationTime
? {
expirationTime: expirationTime.toISOString(),
}
: {}),
...(issuedAt
? {
issuedAt: issuedAt.toISOString(),
}
: {}),
...(notBefore
? {
notBefore: notBefore.toISOString(),
}
: {}),
}
: undefined
return {
...rest,
...(addSubAccount
? {
addSubAccount,
}
: {}),
...(getSubAccounts
? {
getSubAccounts,
}
: {}),
...(signInWithEthereum
? {
signInWithEthereum,
}
: {}),
}
}
function formatResponseCapabilities(
capabilities: ExtractCapabilities<'connect', 'ReturnType'> | undefined,
) {
return Object.entries(capabilities ?? {}).reduce(
(capabilities, [key, value]) => {
const k = (() => {
if (key === 'signInWithEthereum') return 'unstable_signInWithEthereum'
if (key === 'subAccounts') return 'unstable_subAccounts'
return key
})()
capabilities[k] = value
return capabilities
},
{} as Record<string, unknown>,
)
}

View File

@@ -0,0 +1,36 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Chain } from '../../../types/chain.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
export type DisconnectErrorType = RequestErrorType
/**
* Requests to disconnect connected accounts.
*
* - Docs: https://viem.sh/experimental/erc7846/disconnect
* - JSON-RPC Methods: [`wallet_disconnect`](https://github.com/ethereum/ERCs/blob/abd1c9f4eda2d6ad06ade0e3af314637a27d1ee7/ERCS/erc-7846.md)
*
* @param client - Client to use
* @returns void
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { disconnect } from 'viem/experimental/erc7846'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
*
* await disconnect(client)
*/
export async function disconnect<chain extends Chain | undefined>(
client: Client<Transport, chain>,
) {
return await client.request(
{ method: 'wallet_disconnect' },
{ dedupe: true, retryCount: 0 },
)
}

View File

@@ -0,0 +1,88 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Chain } from '../../../types/chain.js'
import {
type ConnectParameters,
type ConnectReturnType,
connect,
} from '../actions/connect.js'
import { disconnect } from '../actions/disconnect.js'
export type Erc7846Actions = {
/**
* Requests to connect to a wallet.
*
* - Docs: https://viem.sh/experimental/erc7846/connect
* - JSON-RPC Methods: [`wallet_connect`](https://github.com/ethereum/ERCs/blob/abd1c9f4eda2d6ad06ade0e3af314637a27d1ee7/ERCS/erc-7846.md)
*
* @param client - Client to use
* @param parameters - {@link ConnectParameters}
* @returns List of accounts managed by a wallet {@link ConnectReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7846Actions } from 'viem/experimental/erc7846'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(erc7846Actions())
*
* const response = await client.connect()
*/
connect: (
parameters?: ConnectParameters | undefined,
) => Promise<ConnectReturnType>
/**
* Requests to disconnect connected accounts.
*
* - Docs: https://viem.sh/experimental/erc7846/disconnect
* - JSON-RPC Methods: [`wallet_disconnect`](https://github.com/ethereum/ERCs/blob/abd1c9f4eda2d6ad06ade0e3af314637a27d1ee7/ERCS/erc-7846.md)
*
* @param client - Client to use
* @returns void
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7846Actions } from 'viem/experimental/erc7846'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(erc7846Actions())
*
* await client.disconnect()
*/
disconnect: () => Promise<void>
}
/**
* A suite of ERC-7846 Wallet Actions.
*
* @example
* import { createPublicClient, createWalletClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7846Actions } from 'viem/experimental/erc7846'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7846Actions())
*
* const response = await client.connect()
*/
export function erc7846Actions() {
return <
transport extends Transport,
chain extends Chain | undefined = Chain | undefined,
>(
client: Client<transport, chain>,
): Erc7846Actions => {
return {
connect: (parameters) => connect(client, parameters),
disconnect: () => disconnect(client),
}
}
}

13
node_modules/viem/experimental/erc7846/index.ts generated vendored Normal file
View File

@@ -0,0 +1,13 @@
// biome-ignore lint/performance/noBarrelFile: entrypoint
export {
type ConnectErrorType,
type ConnectParameters,
type ConnectReturnType,
connect,
} from './actions/connect.js'
export {
type DisconnectErrorType,
disconnect,
} from './actions/disconnect.js'
export { type Erc7846Actions, erc7846Actions } from './decorators/erc7846.js'

6
node_modules/viem/experimental/erc7846/package.json generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"type": "module",
"types": "../../_types/experimental/erc7846/index.d.ts",
"module": "../../_esm/experimental/erc7846/index.js",
"main": "../../_cjs/experimental/erc7846/index.js"
}

View File

@@ -0,0 +1,86 @@
import type { Address } from 'abitype'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Chain } from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
import type { OneOf, Prettify } from '../../../types/utils.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
import { numberToHex } from '../../../utils/index.js'
export type AddSubAccountParameters = Prettify<
OneOf<
| {
keys?:
| readonly {
publicKey: Hex
type: 'address' | 'p256' | 'webcrypto-p256' | 'webauthn-p256'
}[]
| undefined
type: 'create'
}
| {
address: Address
chainId?: number | undefined
type: 'deployed'
}
| {
address: Address
chainId?: number | undefined
factory: Address
factoryData: Hex
type: 'undeployed'
}
>
>
export type AddSubAccountReturnType = Prettify<{
address: Address
factory?: Address | undefined
factoryData?: Hex | undefined
}>
export type AddSubAccountErrorType = RequestErrorType
/**
* Requests to add a Sub Account.
*
* - Docs: https://viem.sh/experimental/erc7895/addSubAccount
* - JSON-RPC Methods: [`wallet_addSubAccount`](https://github.com/ethereum/ERCs/blob/abd1c9f4eda2d6ad06ade0e3af314637a27d1ee7/ERCS/erc-7895.md)
*
* @param client - Client to use
* @param parameters - {@link AddSubAccountParameters}
* @returns Sub Account. {@link AddSubAccountReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { addSubAccount } from 'viem/experimental/erc7895'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* })
* const response = await addSubAccount(client, {
* keys: [{ publicKey: '0x0000000000000000000000000000000000000000', type: 'address' }],
* type: 'create',
* })
*/
export async function addSubAccount<chain extends Chain | undefined>(
client: Client<Transport, chain>,
parameters: AddSubAccountParameters,
): Promise<AddSubAccountReturnType> {
return client.request({
method: 'wallet_addSubAccount',
params: [
{
account: {
...parameters,
...(parameters.chainId
? { chainId: numberToHex(parameters.chainId) }
: {}),
} as never,
version: '1',
},
],
})
}

View File

@@ -0,0 +1,70 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Chain } from '../../../types/chain.js'
import {
type AddSubAccountParameters,
type AddSubAccountReturnType,
addSubAccount,
} from '../actions/addSubAccount.js'
export type Erc7895Actions = {
/**
* Requests to add a Sub Account.
*
* - Docs: https://viem.sh/experimental/erc7895/addSubAccount
* - JSON-RPC Methods: [`wallet_addSubAccount`](https://github.com/ethereum/ERCs/blob/abd1c9f4eda2d6ad06ade0e3af314637a27d1ee7/ERCS/erc-7895.md)
*
* @param client - Client to use
* @param parameters - {@link AddSubAccountParameters}
* @returns Sub Account. {@link AddSubAccountReturnType}
*
* @example
* import { createWalletClient, custom } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7895Actions } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: custom(window.ethereum),
* }).extend(erc7895Actions())
*
* const response = await client.addSubAccount({
* keys: [{ key: '0x0000000000000000000000000000000000000000', type: 'address' }],
* type: 'create',
* })
*/
addSubAccount: (
parameters: AddSubAccountParameters,
) => Promise<AddSubAccountReturnType>
}
/**
* A suite of ERC-7895 Wallet Actions.
*
* @example
* import { createPublicClient, createWalletClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7895Actions } from 'viem/experimental'
*
* const client = createWalletClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7895Actions())
*
* const response = await client.addSubAccount({
* keys: [{ key: '0x0000000000000000000000000000000000000000', type: 'address' }],
* type: 'create',
* })
*/
export function erc7895Actions() {
return <
transport extends Transport,
chain extends Chain | undefined = Chain | undefined,
>(
client: Client<transport, chain>,
): Erc7895Actions => {
return {
addSubAccount: (parameters) => addSubAccount(client, parameters),
}
}
}

9
node_modules/viem/experimental/erc7895/index.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// biome-ignore lint/performance/noBarrelFile: entrypoint
export {
type AddSubAccountErrorType,
type AddSubAccountParameters,
type AddSubAccountReturnType,
addSubAccount,
} from './actions/addSubAccount.js'
export { type Erc7895Actions, erc7895Actions } from './decorators/erc7895.js'

6
node_modules/viem/experimental/erc7895/package.json generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"type": "module",
"types": "../../_types/experimental/erc7895/index.d.ts",
"module": "../../_esm/experimental/erc7895/index.js",
"main": "../../_cjs/experimental/erc7895/index.js"
}

189
node_modules/viem/experimental/index.ts generated vendored Normal file
View File

@@ -0,0 +1,189 @@
// biome-ignore lint/performance/noBarrelFile: entrypoint module
export {
/** @deprecated This is no longer experimental use `import type { GetCallsStatusErrorType } from 'viem/actions'` instead. */
type GetCallsStatusErrorType,
/** @deprecated This is no longer experimental use `import type { GetCallsStatusParameters } from 'viem/actions'` instead. */
type GetCallsStatusParameters,
/** @deprecated This is no longer experimental use `import type { GetCallsStatusReturnType } from 'viem/actions'` instead. */
type GetCallsStatusReturnType,
/** @deprecated This is no longer experimental use `import { getCallsStatus } from 'viem/actions'` instead. */
getCallsStatus,
} from '../actions/wallet/getCallsStatus.js'
export {
/** @deprecated This is no longer experimental use `import type { GetCapabilitiesErrorType } from 'viem/actions'` instead. */
type GetCapabilitiesErrorType,
/** @deprecated This is no longer experimental use `import type { GetCapabilitiesParameters } from 'viem/actions'` instead. */
type GetCapabilitiesParameters,
/** @deprecated This is no longer experimental use `import type { GetCapabilitiesReturnType } from 'viem/actions'` instead. */
type GetCapabilitiesReturnType,
/** @deprecated This is no longer experimental use `import { getCapabilities } from 'viem/actions'` instead. */
getCapabilities,
} from '../actions/wallet/getCapabilities.js'
export {
/** @deprecated This is no longer experimental use `import type { PrepareAuthorizationErrorType } from 'viem/actions'` instead. */
type PrepareAuthorizationErrorType,
/** @deprecated This is no longer experimental use `import type { PrepareAuthorizationParameters } from 'viem/actions'` instead. */
type PrepareAuthorizationParameters,
/** @deprecated This is no longer experimental use `import type { PrepareAuthorizationReturnType } from 'viem/actions'` instead. */
type PrepareAuthorizationReturnType,
/** @deprecated This is no longer experimental use `import { prepareAuthorization } from 'viem/actions'` instead. */
prepareAuthorization,
} from '../actions/wallet/prepareAuthorization.js'
export {
/** @deprecated This is no longer experimental use `import type { SendCallsErrorType } from 'viem/actions'` instead. */
type SendCallsErrorType,
/** @deprecated This is no longer experimental use `import type { SendCallsParameters } from 'viem/actions'` instead. */
type SendCallsParameters,
/** @deprecated This is no longer experimental use `import type { SendCallsReturnType } from 'viem/actions'` instead. */
type SendCallsReturnType,
/** @deprecated This is no longer experimental use `import { sendCalls } from 'viem/actions'` instead. */
sendCalls,
} from '../actions/wallet/sendCalls.js'
export {
/** @deprecated This is no longer experimental use `import type { ShowCallsStatusErrorType } from 'viem/actions'` instead. */
type ShowCallsStatusErrorType,
/** @deprecated This is no longer experimental use `import type { ShowCallsStatusParameters } from 'viem/actions'` instead. */
type ShowCallsStatusParameters,
/** @deprecated This is no longer experimental use `import type { ShowCallsStatusReturnType } from 'viem/actions'` instead. */
type ShowCallsStatusReturnType,
/** @deprecated This is no longer experimental use `import { showCallsStatus } from 'viem/actions'` instead. */
showCallsStatus,
} from '../actions/wallet/showCallsStatus.js'
export {
/** @deprecated This is no longer experimental use `import type { SignAuthorizationErrorType } from 'viem/actions'` instead. */
type SignAuthorizationErrorType,
/** @deprecated This is no longer experimental use `import type { SignAuthorizationParameters } from 'viem/actions'` instead. */
type SignAuthorizationParameters,
/** @deprecated This is no longer experimental use `import type { SignAuthorizationReturnType } from 'viem/actions'` instead. */
type SignAuthorizationReturnType,
/** @deprecated This is no longer experimental use `import { signAuthorization } from 'viem/actions'` instead. */
signAuthorization,
} from '../actions/wallet/signAuthorization.js'
export {
/** @deprecated This is no longer experimental use `import type { WaitForCallsStatusErrorType } from 'viem/actions'` instead. */
type WaitForCallsStatusErrorType,
/** @deprecated This is no longer experimental use `import type { WaitForCallsStatusParameters } from 'viem/actions'` instead. */
type WaitForCallsStatusParameters,
/** @deprecated This is no longer experimental use `import type { WaitForCallsStatusReturnType } from 'viem/actions'` instead. */
type WaitForCallsStatusReturnType,
/** @deprecated This is no longer experimental use `import type { WaitForCallsStatusTimeoutErrorType } from 'viem/actions'` instead. */
type WaitForCallsStatusTimeoutErrorType,
/** @deprecated This is no longer experimental use `import { waitForCallsStatus } from 'viem/actions'` instead. */
waitForCallsStatus,
} from '../actions/wallet/waitForCallsStatus.js'
export {
/** @deprecated This is no longer experimental use `import type { WalletActions } from 'viem'` instead. */
type WalletActions as Eip7702Actions,
/** @deprecated This is no longer experimental use `import { createWalletClient } from 'viem'` or `import { walletActions } from 'viem'` instead. */
walletActions as eip7702Actions,
} from '../clients/decorators/wallet.js'
export type {
/** @deprecated This is no longer experimental use `import type { Authorization } from 'viem'` instead. */
Authorization,
/** @deprecated This is no longer experimental use `import type { AuthorizationList } from 'viem'` instead. */
AuthorizationList,
/** @deprecated This is no longer experimental use `import type { SerializedAuthorization } from 'viem'` instead. */
SerializedAuthorization,
/** @deprecated This is no longer experimental use `import type { SerializedAuthorizationList } from 'viem'` instead. */
SerializedAuthorizationList,
/** @deprecated This is no longer experimental use `import type { SignedAuthorization } from 'viem'` instead. */
SignedAuthorization,
/** @deprecated This is no longer experimental use `import type { SignedAuthorizationList } from 'viem'` instead. */
SignedAuthorizationList,
} from '../types/authorization.js'
export type {
/** @deprecated This is no longer experimental use `import type { RpcAuthorization } from 'viem'` instead. */
RpcAuthorization,
/** @deprecated This is no longer experimental use `import type { RpcAuthorizationList } from 'viem'` instead. */
RpcAuthorizationList,
} from '../types/rpc.js'
export {
/** @deprecated This is no longer experimental use `import type { HashAuthorizationErrorType } from 'viem/utils'` instead. */
type HashAuthorizationErrorType,
/** @deprecated This is no longer experimental use `import type { HashAuthorizationParameters } from 'viem/utils'` instead. */
type HashAuthorizationParameters,
/** @deprecated This is no longer experimental use `import type { HashAuthorizationReturnType } from 'viem/utils'` instead. */
type HashAuthorizationReturnType,
/** @deprecated This is no longer experimental use `import { hashAuthorization } from 'viem/utils'` instead. */
hashAuthorization,
} from '../utils/authorization/hashAuthorization.js'
export {
/** @deprecated This is no longer experimental use `import type { RecoverAuthorizationAddressErrorType } from 'viem/utils'` instead. */
type RecoverAuthorizationAddressErrorType,
/** @deprecated This is no longer experimental use `import type { RecoverAuthorizationAddressParameters } from 'viem/utils'` instead. */
type RecoverAuthorizationAddressParameters,
/** @deprecated This is no longer experimental use `import type { RecoverAuthorizationAddressReturnType } from 'viem/utils'` instead. */
type RecoverAuthorizationAddressReturnType,
/** @deprecated This is no longer experimental use `import { recoverAuthorizationAddress } from 'viem/utils'` instead. */
recoverAuthorizationAddress,
} from '../utils/authorization/recoverAuthorizationAddress.js'
export {
/** @deprecated This is no longer experimental use `import type { SerializeAuthorizationListErrorType } from 'viem/utils'` instead. */
type SerializeAuthorizationListErrorType,
/** @deprecated This is no longer experimental use `import type { SerializeAuthorizationListReturnType } from 'viem/utils'` instead. */
type SerializeAuthorizationListReturnType,
/** @deprecated This is no longer experimental use `import { serializeAuthorizationList } from 'viem/utils'` instead. */
serializeAuthorizationList,
} from '../utils/authorization/serializeAuthorizationList.js'
export {
/** @deprecated This is no longer experimental use `import type { VerifyAuthorizationErrorType } from 'viem/utils'` instead. */
type VerifyAuthorizationErrorType,
/** @deprecated This is no longer experimental use `import type { VerifyAuthorizationParameters } from 'viem/utils'` instead. */
type VerifyAuthorizationParameters,
/** @deprecated This is no longer experimental use `import type { VerifyAuthorizationReturnType } from 'viem/utils'` instead. */
type VerifyAuthorizationReturnType,
/** @deprecated This is no longer experimental use `import { verifyAuthorization } from 'viem/utils'` instead. */
verifyAuthorization,
} from '../utils/authorization/verifyAuthorization.js'
export {
/** @deprecated */
type WriteContractFunctionParameters,
/** @deprecated Use `SendCallsErrorType` instead. */
type WriteContractsErrorType,
/** @deprecated Use `SendCallsParameters` instead. */
type WriteContractsParameters,
/** @deprecated Use `SendCallsReturnType` instead. */
type WriteContractsReturnType,
/** @deprecated Use `sendCalls` instead. */
writeContracts,
} from './eip5792/actions/writeContracts.js'
export {
/** @deprecated This is no longer experimental use `import type { WalletActions } from 'viem'` instead. */
type Eip5792Actions,
/** @deprecated This is no longer experimental use `import { createWalletClient } from 'viem'` or `import { walletActions } from 'viem'` instead. */
eip5792Actions,
} from './eip5792/decorators/eip5792.js'
export {
type GrantPermissionsParameters,
type GrantPermissionsReturnType,
grantPermissions,
} from './erc7715/actions/grantPermissions.js'
export {
type Erc7715Actions,
erc7715Actions,
} from './erc7715/decorators/erc7715.js'
export {
type Erc7739Actions,
type Erc7739ActionsParameters,
erc7739Actions,
} from './erc7739/decorators/erc7739.js'
export {
type Erc7811Actions,
erc7811Actions,
} from './erc7811/decorators/erc7811.js'
export {
type Erc7821Actions,
erc7821Actions,
} from './erc7821/decorators/erc7821.js'
export {
type Erc7846Actions,
erc7846Actions,
} from './erc7846/decorators/erc7846.js'
export {
type Erc7895Actions,
erc7895Actions,
} from './erc7895/decorators/erc7895.js'

6
node_modules/viem/experimental/package.json generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"type": "module",
"types": "../_types/experimental/index.d.ts",
"module": "../_esm/experimental/index.js",
"main": "../_cjs/experimental/index.js"
}