viem 2.57.8 → 2.57.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/_cjs/clients/createClient.js +49 -4
- package/_cjs/clients/createClient.js.map +1 -1
- package/_cjs/clients/transports/fallback.js +2 -2
- package/_cjs/clients/transports/fallback.js.map +1 -1
- package/_cjs/errors/version.js +1 -1
- package/_cjs/errors/version.js.map +1 -1
- package/_cjs/experimental/erc7821/actions/supportsExecutionMode.js +1 -1
- package/_cjs/experimental/erc7821/actions/supportsExecutionMode.js.map +1 -1
- package/_cjs/op-stack/actions/getPortalVersion.js +4 -1
- package/_cjs/op-stack/actions/getPortalVersion.js.map +1 -1
- package/_cjs/tempo/Abis.js +84 -15
- package/_cjs/tempo/Abis.js.map +1 -1
- package/_cjs/tempo/Account.js +13 -11
- package/_cjs/tempo/Account.js.map +1 -1
- package/_cjs/tempo/Addresses.js +3 -3
- package/_cjs/tempo/Addresses.js.map +1 -1
- package/_cjs/tempo/Decorator.js +4 -0
- package/_cjs/tempo/Decorator.js.map +1 -1
- package/_cjs/tempo/ExecutionError.js +25 -2
- package/_cjs/tempo/ExecutionError.js.map +1 -1
- package/_cjs/tempo/Hardfork.js +12 -1
- package/_cjs/tempo/Hardfork.js.map +1 -1
- package/_cjs/tempo/Selectors.js +4 -2
- package/_cjs/tempo/Selectors.js.map +1 -1
- package/_cjs/tempo/accounts/Config.js +4 -2
- package/_cjs/tempo/accounts/Config.js.map +1 -1
- package/_cjs/tempo/accounts/Operation.js +2 -2
- package/_cjs/tempo/accounts/Operation.js.map +1 -1
- package/_cjs/tempo/actions/accessKey.js +1 -0
- package/_cjs/tempo/actions/accessKey.js.map +1 -1
- package/_cjs/tempo/actions/accounts.js +122 -7
- package/_cjs/tempo/actions/accounts.js.map +1 -1
- package/_cjs/tempo/chainConfig.js +29 -2
- package/_cjs/tempo/chainConfig.js.map +1 -1
- package/_cjs/tempo/internal/relay/accounts.js +46 -5
- package/_cjs/tempo/internal/relay/accounts.js.map +1 -1
- package/_cjs/tempo/internal/relay/feePayer.js +4 -4
- package/_cjs/tempo/internal/relay/feePayer.js.map +1 -1
- package/_cjs/utils/promise/withRetry.js +5 -1
- package/_cjs/utils/promise/withRetry.js.map +1 -1
- package/_esm/clients/createClient.js +57 -4
- package/_esm/clients/createClient.js.map +1 -1
- package/_esm/clients/transports/fallback.js +2 -2
- package/_esm/clients/transports/fallback.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_esm/errors/version.js.map +1 -1
- package/_esm/experimental/erc7821/actions/supportsExecutionMode.js +2 -1
- package/_esm/experimental/erc7821/actions/supportsExecutionMode.js.map +1 -1
- package/_esm/op-stack/actions/getPortalVersion.js +4 -1
- package/_esm/op-stack/actions/getPortalVersion.js.map +1 -1
- package/_esm/tempo/Abis.js +84 -15
- package/_esm/tempo/Abis.js.map +1 -1
- package/_esm/tempo/Account.js +18 -12
- package/_esm/tempo/Account.js.map +1 -1
- package/_esm/tempo/Addresses.js +3 -3
- package/_esm/tempo/Addresses.js.map +1 -1
- package/_esm/tempo/Decorator.js +4 -0
- package/_esm/tempo/Decorator.js.map +1 -1
- package/_esm/tempo/ExecutionError.js +25 -2
- package/_esm/tempo/ExecutionError.js.map +1 -1
- package/_esm/tempo/Hardfork.js +17 -2
- package/_esm/tempo/Hardfork.js.map +1 -1
- package/_esm/tempo/Selectors.js +3 -1
- package/_esm/tempo/Selectors.js.map +1 -1
- package/_esm/tempo/accounts/Config.js +5 -3
- package/_esm/tempo/accounts/Config.js.map +1 -1
- package/_esm/tempo/accounts/Operation.js +2 -2
- package/_esm/tempo/accounts/Operation.js.map +1 -1
- package/_esm/tempo/actions/accessKey.js +1 -0
- package/_esm/tempo/actions/accessKey.js.map +1 -1
- package/_esm/tempo/actions/accounts.js +279 -8
- package/_esm/tempo/actions/accounts.js.map +1 -1
- package/_esm/tempo/chainConfig.js +33 -3
- package/_esm/tempo/chainConfig.js.map +1 -1
- package/_esm/tempo/internal/relay/accounts.js +50 -5
- package/_esm/tempo/internal/relay/accounts.js.map +1 -1
- package/_esm/tempo/internal/relay/feePayer.js +4 -4
- package/_esm/tempo/internal/relay/feePayer.js.map +1 -1
- package/_esm/utils/promise/withRetry.js +6 -1
- package/_esm/utils/promise/withRetry.js.map +1 -1
- package/_types/clients/createClient.d.ts +30 -0
- package/_types/clients/createClient.d.ts.map +1 -1
- package/_types/clients/transports/fallback.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/_types/errors/version.d.ts.map +1 -1
- package/_types/experimental/erc7821/actions/supportsExecutionMode.d.ts.map +1 -1
- package/_types/op-stack/actions/getPortalVersion.d.ts.map +1 -1
- package/_types/tempo/Abis.d.ts +621 -307
- package/_types/tempo/Abis.d.ts.map +1 -1
- package/_types/tempo/Account.d.ts +24 -4
- package/_types/tempo/Account.d.ts.map +1 -1
- package/_types/tempo/Addresses.d.ts +3 -3
- package/_types/tempo/Addresses.d.ts.map +1 -1
- package/_types/tempo/Decorator.d.ts +104 -0
- package/_types/tempo/Decorator.d.ts.map +1 -1
- package/_types/tempo/ExecutionError.d.ts.map +1 -1
- package/_types/tempo/Hardfork.d.ts +6 -2
- package/_types/tempo/Hardfork.d.ts.map +1 -1
- package/_types/tempo/Selectors.d.ts +3 -1
- package/_types/tempo/Selectors.d.ts.map +1 -1
- package/_types/tempo/Transaction.d.ts +2 -2
- package/_types/tempo/Transaction.d.ts.map +1 -1
- package/_types/tempo/actions/accessKey.d.ts +3 -2
- package/_types/tempo/actions/accessKey.d.ts.map +1 -1
- package/_types/tempo/actions/accounts.d.ts +594 -7
- package/_types/tempo/actions/accounts.d.ts.map +1 -1
- package/_types/tempo/actions/channel.d.ts +42 -0
- package/_types/tempo/actions/channel.d.ts.map +1 -1
- package/_types/tempo/actions/reward.d.ts +14 -0
- package/_types/tempo/actions/reward.d.ts.map +1 -1
- package/_types/tempo/actions/token.d.ts +105 -0
- package/_types/tempo/actions/token.d.ts.map +1 -1
- package/_types/tempo/chainConfig.d.ts.map +1 -1
- package/_types/tempo/internal/relay/accounts.d.ts.map +1 -1
- package/_types/utils/promise/withRetry.d.ts.map +1 -1
- package/clients/createClient.ts +103 -5
- package/clients/transports/fallback.ts +8 -5
- package/errors/version.ts +1 -1
- package/experimental/erc7821/actions/supportsExecutionMode.ts +2 -1
- package/op-stack/actions/getPortalVersion.ts +4 -1
- package/package.json +2 -2
- package/tempo/Abis.ts +84 -15
- package/tempo/Account.ts +52 -16
- package/tempo/Addresses.ts +4 -4
- package/tempo/Decorator.ts +116 -0
- package/tempo/ExecutionError.ts +28 -2
- package/tempo/Hardfork.ts +16 -2
- package/tempo/Selectors.ts +4 -2
- package/tempo/Transaction.ts +2 -2
- package/tempo/accounts/Config.ts +5 -3
- package/tempo/accounts/Operation.ts +2 -2
- package/tempo/actions/accessKey.ts +3 -2
- package/tempo/actions/accounts.ts +394 -10
- package/tempo/chainConfig.ts +41 -3
- package/tempo/internal/relay/accounts.ts +62 -5
- package/tempo/internal/relay/feePayer.ts +4 -4
- package/utils/promise/withRetry.ts +7 -1
|
@@ -72,6 +72,7 @@ const signatureTypes = {
|
|
|
72
72
|
0: 'secp256k1',
|
|
73
73
|
1: 'p256',
|
|
74
74
|
2: 'webAuthn',
|
|
75
|
+
3: 'configurable',
|
|
75
76
|
} as const satisfies Record<number, string>
|
|
76
77
|
|
|
77
78
|
/** @internal */
|
|
@@ -558,8 +559,8 @@ export namespace getMetadata {
|
|
|
558
559
|
export type ReturnValue = {
|
|
559
560
|
/** The access key address. */
|
|
560
561
|
address: Address
|
|
561
|
-
/** The key type. */
|
|
562
|
-
keyType: 'secp256k1' | 'p256' | 'webAuthn'
|
|
562
|
+
/** The key type. `configurable` for a configurable account access key. */
|
|
563
|
+
keyType: 'secp256k1' | 'p256' | 'webAuthn' | 'configurable'
|
|
563
564
|
/** The expiry timestamp. */
|
|
564
565
|
expiry: bigint
|
|
565
566
|
/** The spending policy. */
|
|
@@ -13,7 +13,7 @@ import type { Transport } from '../../clients/transports/createTransport.js'
|
|
|
13
13
|
import type { BaseErrorType } from '../../errors/base.js'
|
|
14
14
|
import type { Chain } from '../../types/chain.js'
|
|
15
15
|
import type { Log } from '../../types/log.js'
|
|
16
|
-
import type { Compute } from '../../types/utils.js'
|
|
16
|
+
import type { Compute, OneOf, UnionOmit } from '../../types/utils.js'
|
|
17
17
|
import { parseEventLogs } from '../../utils/abi/parseEventLogs.js'
|
|
18
18
|
import * as Abis from '../Abis.js'
|
|
19
19
|
import { type ConfigurableAccount, fromConfig } from '../Account.js'
|
|
@@ -24,6 +24,88 @@ import type { ReadParameters, WriteParameters } from '../internal/types.js'
|
|
|
24
24
|
import { defineCall } from '../internal/utils.js'
|
|
25
25
|
import type * as Transaction from '../Transaction.js'
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Derives a configurable account address from an initial config with the
|
|
29
|
+
* configurable account precompile.
|
|
30
|
+
*
|
|
31
|
+
* The precompile validates the config and derives the address with the chain's
|
|
32
|
+
* recovery factory. To derive the address locally, use
|
|
33
|
+
* `AccountConfig.getAddress` or `Account.fromConfig({ address: 'infer' })`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { createClient, http } from 'viem'
|
|
38
|
+
* import { tempo } from 'viem/chains'
|
|
39
|
+
* import { Actions } from 'viem/tempo'
|
|
40
|
+
*
|
|
41
|
+
* const client = createClient({
|
|
42
|
+
* chain: tempo,
|
|
43
|
+
* transport: http(),
|
|
44
|
+
* })
|
|
45
|
+
*
|
|
46
|
+
* const address = await Actions.accounts.deriveAccount(client, {
|
|
47
|
+
* owners: [{ owner: '0x...', weight: 1 }],
|
|
48
|
+
* threshold: 1,
|
|
49
|
+
* })
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @param client - Client.
|
|
53
|
+
* @param parameters - Parameters.
|
|
54
|
+
* @returns The configurable account address.
|
|
55
|
+
*/
|
|
56
|
+
export async function deriveAccount<
|
|
57
|
+
chain extends Chain | undefined,
|
|
58
|
+
account extends Account | undefined,
|
|
59
|
+
>(
|
|
60
|
+
client: Client<Transport, chain, account>,
|
|
61
|
+
parameters: deriveAccount.Parameters,
|
|
62
|
+
): Promise<deriveAccount.ReturnValue> {
|
|
63
|
+
const { owners, salt, threshold, ...rest } = parameters
|
|
64
|
+
return withExecutionError(readContract)(client, {
|
|
65
|
+
...rest,
|
|
66
|
+
...deriveAccount.call({ owners, salt, threshold }),
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export namespace deriveAccount {
|
|
71
|
+
export type Parameters = ReadParameters & Args
|
|
72
|
+
|
|
73
|
+
export type Args = {
|
|
74
|
+
/** Weighted owners. Normalized into canonical address order. */
|
|
75
|
+
owners: AccountConfig.Config['owners']
|
|
76
|
+
/** Caller-chosen 32-byte salt. Defaults to the zero salt. */
|
|
77
|
+
salt?: Hex.Hex | undefined
|
|
78
|
+
/** Minimum total owner weight required for authorization. */
|
|
79
|
+
threshold: number
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type ReturnValue = ReadContractReturnType<
|
|
83
|
+
typeof Abis.configurableAccount,
|
|
84
|
+
'deriveAccount',
|
|
85
|
+
never
|
|
86
|
+
>
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Defines a call to the `deriveAccount` function.
|
|
90
|
+
*
|
|
91
|
+
* Can be passed to [`multicall`](https://viem.sh/docs/contract/multicall).
|
|
92
|
+
*
|
|
93
|
+
* @param args - Arguments.
|
|
94
|
+
* @returns The call.
|
|
95
|
+
*/
|
|
96
|
+
export function call(args: Args) {
|
|
97
|
+
const { owners, salt, threshold } = AccountConfig.from(args)
|
|
98
|
+
return defineCall({
|
|
99
|
+
address: Addresses.configurableAccount,
|
|
100
|
+
abi: Abis.configurableAccount,
|
|
101
|
+
args: [salt, threshold, owners],
|
|
102
|
+
functionName: 'deriveAccount',
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type ErrorType = Errors.ExecutionErrorType | BaseErrorType
|
|
107
|
+
}
|
|
108
|
+
|
|
27
109
|
/**
|
|
28
110
|
* Gets the current cached config for a configurable account.
|
|
29
111
|
*
|
|
@@ -118,7 +200,7 @@ export namespace getConfigCommitment {
|
|
|
118
200
|
}
|
|
119
201
|
|
|
120
202
|
export type ReturnValue = ReadContractReturnType<
|
|
121
|
-
typeof Abis.
|
|
203
|
+
typeof Abis.configurableAccount,
|
|
122
204
|
'getConfigCommitment',
|
|
123
205
|
never
|
|
124
206
|
>
|
|
@@ -133,8 +215,8 @@ export namespace getConfigCommitment {
|
|
|
133
215
|
*/
|
|
134
216
|
export function call(args: Args) {
|
|
135
217
|
return defineCall({
|
|
136
|
-
address: Addresses.
|
|
137
|
-
abi: Abis.
|
|
218
|
+
address: Addresses.configurableAccount,
|
|
219
|
+
abi: Abis.configurableAccount,
|
|
138
220
|
args: [args.account],
|
|
139
221
|
functionName: 'getConfigCommitment',
|
|
140
222
|
})
|
|
@@ -348,27 +430,28 @@ export namespace updateConfig {
|
|
|
348
430
|
const currentConfig = AccountConfig.from(args.currentConfig)
|
|
349
431
|
const nextConfig = AccountConfig.update(currentConfig, args.nextConfig)
|
|
350
432
|
return defineCall({
|
|
351
|
-
address: Addresses.
|
|
352
|
-
abi: Abis.
|
|
433
|
+
address: Addresses.configurableAccount,
|
|
434
|
+
abi: Abis.configurableAccount,
|
|
353
435
|
args: [currentConfig, nextConfig.threshold, nextConfig.owners],
|
|
354
436
|
functionName: 'updateConfig',
|
|
355
437
|
})
|
|
356
438
|
}
|
|
357
439
|
|
|
358
440
|
/**
|
|
359
|
-
* Extracts the `
|
|
441
|
+
* Extracts the `ConfigurableAccountConfigUpdated` event from logs.
|
|
360
442
|
*
|
|
361
443
|
* @param logs - Transaction logs.
|
|
362
444
|
* @returns The configuration update event.
|
|
363
445
|
*/
|
|
364
446
|
export function extractEvent(logs: Log[]) {
|
|
365
447
|
const [log] = parseEventLogs({
|
|
366
|
-
abi: Abis.
|
|
448
|
+
abi: Abis.configurableAccount,
|
|
367
449
|
logs,
|
|
368
|
-
eventName: '
|
|
450
|
+
eventName: 'ConfigurableAccountConfigUpdated',
|
|
369
451
|
strict: true,
|
|
370
452
|
})
|
|
371
|
-
if (!log)
|
|
453
|
+
if (!log)
|
|
454
|
+
throw new Error('`ConfigurableAccountConfigUpdated` event not found.')
|
|
372
455
|
return log
|
|
373
456
|
}
|
|
374
457
|
}
|
|
@@ -447,3 +530,304 @@ export namespace updateConfigSync {
|
|
|
447
530
|
|
|
448
531
|
export type ErrorType = updateConfig.ErrorType
|
|
449
532
|
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Upgrades an existing account to a configurable account in place
|
|
536
|
+
* ([TIP-1113](https://tips.sh/1113)).
|
|
537
|
+
*
|
|
538
|
+
* The account keeps its address and installs `owners` and `threshold`. The
|
|
539
|
+
* root key must sign the upgrade, which is sent as the transaction's only
|
|
540
|
+
* call. After the upgrade, the root key can no longer sign for the account.
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```ts
|
|
544
|
+
* import { createClient, http } from 'viem'
|
|
545
|
+
* import { tempoLocalnet } from 'viem/chains'
|
|
546
|
+
* import { Account, Actions } from 'viem/tempo'
|
|
547
|
+
*
|
|
548
|
+
* const root = Account.fromSecp256k1('0x...')
|
|
549
|
+
* const client = createClient({
|
|
550
|
+
* chain: tempoLocalnet,
|
|
551
|
+
* transport: http(),
|
|
552
|
+
* })
|
|
553
|
+
*
|
|
554
|
+
* const hash = await Actions.accounts.upgrade(client, {
|
|
555
|
+
* account: root,
|
|
556
|
+
* owners: [{ owner: '0x...', weight: 1 }],
|
|
557
|
+
* threshold: 1,
|
|
558
|
+
* })
|
|
559
|
+
* ```
|
|
560
|
+
*
|
|
561
|
+
* @param client - Client.
|
|
562
|
+
* @param parameters - Parameters.
|
|
563
|
+
* @returns The transaction hash.
|
|
564
|
+
*/
|
|
565
|
+
export async function upgrade<
|
|
566
|
+
chain extends Chain | undefined,
|
|
567
|
+
account extends Account | undefined,
|
|
568
|
+
>(
|
|
569
|
+
client: Client<Transport, chain, account>,
|
|
570
|
+
parameters: upgrade.Parameters<chain, account>,
|
|
571
|
+
): Promise<upgrade.ReturnValue> {
|
|
572
|
+
return upgrade.inner(writeContract, client, parameters)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export namespace upgrade {
|
|
576
|
+
export type Parameters<
|
|
577
|
+
chain extends Chain | undefined = Chain | undefined,
|
|
578
|
+
account extends Account | undefined = Account | undefined,
|
|
579
|
+
> = UnionOmit<WriteParameters<chain, account>, 'keyAuthorization' | 'owner'> &
|
|
580
|
+
Args
|
|
581
|
+
|
|
582
|
+
export type Args = Pick<AccountConfig.Config, 'owners' | 'threshold'>
|
|
583
|
+
|
|
584
|
+
export type ReturnValue = WriteContractReturnType
|
|
585
|
+
|
|
586
|
+
// TODO: exhaustive error type
|
|
587
|
+
export type ErrorType = Errors.ExecutionErrorType | BaseErrorType
|
|
588
|
+
|
|
589
|
+
/** @internal */
|
|
590
|
+
export async function inner<
|
|
591
|
+
action extends typeof writeContract | typeof writeContractSync,
|
|
592
|
+
chain extends Chain | undefined,
|
|
593
|
+
account extends Account | undefined,
|
|
594
|
+
>(
|
|
595
|
+
action: action,
|
|
596
|
+
client: Client<Transport, chain, account>,
|
|
597
|
+
parameters: Parameters<chain, account>,
|
|
598
|
+
): Promise<ReturnType<action>> {
|
|
599
|
+
const { owners, threshold, ...rest } = parameters
|
|
600
|
+
for (const key of [
|
|
601
|
+
'authorizationList',
|
|
602
|
+
'calls',
|
|
603
|
+
'data',
|
|
604
|
+
'keyAuthorization',
|
|
605
|
+
'to',
|
|
606
|
+
])
|
|
607
|
+
if ((rest as Record<string, unknown>)[key] !== undefined)
|
|
608
|
+
throw new Error(
|
|
609
|
+
`\`${key}\` is not supported: the upgrade must be the only call, without authorizations.`,
|
|
610
|
+
)
|
|
611
|
+
const account = rest.account ?? client.account
|
|
612
|
+
if (typeof account === 'object') {
|
|
613
|
+
if (account.source === 'configurable')
|
|
614
|
+
throw new Error('The account is already a configurable account.')
|
|
615
|
+
if (account.source === 'accessKey')
|
|
616
|
+
throw new Error(
|
|
617
|
+
'The root key must sign the upgrade, not an access key.',
|
|
618
|
+
)
|
|
619
|
+
}
|
|
620
|
+
// Always send a Tempo envelope, so relays recognize and cache the upgrade.
|
|
621
|
+
return (await withExecutionError(action)(client, {
|
|
622
|
+
...rest,
|
|
623
|
+
...upgrade.call({ owners, threshold }),
|
|
624
|
+
type: 'tempo',
|
|
625
|
+
} as never)) as never
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Defines a call to the `upgradeAccount` function.
|
|
630
|
+
*
|
|
631
|
+
* The call must be the only call in a transaction signed by the account's
|
|
632
|
+
* root key.
|
|
633
|
+
*
|
|
634
|
+
* @example
|
|
635
|
+
* ```ts
|
|
636
|
+
* import { Actions } from 'viem/tempo'
|
|
637
|
+
*
|
|
638
|
+
* const call = Actions.accounts.upgrade.call({
|
|
639
|
+
* owners: [{ owner: '0x...', weight: 1 }],
|
|
640
|
+
* threshold: 1,
|
|
641
|
+
* })
|
|
642
|
+
* ```
|
|
643
|
+
*
|
|
644
|
+
* @param args - Owners and threshold to install.
|
|
645
|
+
* @returns The call.
|
|
646
|
+
*/
|
|
647
|
+
export function call(args: Args) {
|
|
648
|
+
const { owners, threshold } = AccountConfig.from(args)
|
|
649
|
+
return defineCall({
|
|
650
|
+
address: Addresses.configurableAccount,
|
|
651
|
+
abi: Abis.configurableAccount,
|
|
652
|
+
args: [threshold, owners],
|
|
653
|
+
functionName: 'upgradeAccount',
|
|
654
|
+
})
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Extracts the `AccountUpgraded` event from logs.
|
|
659
|
+
*
|
|
660
|
+
* @param logs - Transaction logs.
|
|
661
|
+
* @returns The account upgrade event.
|
|
662
|
+
*/
|
|
663
|
+
export function extractEvent(logs: Log[]) {
|
|
664
|
+
const [log] = parseEventLogs({
|
|
665
|
+
abi: Abis.configurableAccount,
|
|
666
|
+
logs,
|
|
667
|
+
eventName: 'AccountUpgraded',
|
|
668
|
+
strict: true,
|
|
669
|
+
})
|
|
670
|
+
if (!log) throw new Error('`AccountUpgraded` event not found.')
|
|
671
|
+
return log
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Upgrades an existing account to a configurable account in place
|
|
677
|
+
* ([TIP-1113](https://tips.sh/1113)) and waits for confirmation.
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* ```ts
|
|
681
|
+
* import { createClient, http } from 'viem'
|
|
682
|
+
* import { tempoLocalnet } from 'viem/chains'
|
|
683
|
+
* import { Account, Actions } from 'viem/tempo'
|
|
684
|
+
*
|
|
685
|
+
* const root = Account.fromSecp256k1('0x...')
|
|
686
|
+
* const client = createClient({
|
|
687
|
+
* chain: tempoLocalnet,
|
|
688
|
+
* transport: http(),
|
|
689
|
+
* })
|
|
690
|
+
*
|
|
691
|
+
* const { config } = await Actions.accounts.upgradeSync(client, {
|
|
692
|
+
* account: root,
|
|
693
|
+
* owners: [{ owner: '0x...', weight: 1 }],
|
|
694
|
+
* threshold: 1,
|
|
695
|
+
* })
|
|
696
|
+
* ```
|
|
697
|
+
*
|
|
698
|
+
* @param client - Client.
|
|
699
|
+
* @param parameters - Parameters.
|
|
700
|
+
* @returns The installed configuration, its commitment, and the transaction receipt.
|
|
701
|
+
*/
|
|
702
|
+
export async function upgradeSync<
|
|
703
|
+
chain extends Chain | undefined,
|
|
704
|
+
account extends Account | undefined,
|
|
705
|
+
>(
|
|
706
|
+
client: Client<Transport, chain, account>,
|
|
707
|
+
parameters: upgradeSync.Parameters<chain, account>,
|
|
708
|
+
): Promise<upgradeSync.ReturnValue> {
|
|
709
|
+
const { throwOnReceiptRevert = true, ...rest } = parameters
|
|
710
|
+
const receipt = await upgrade.inner(writeContractSync, client, {
|
|
711
|
+
...rest,
|
|
712
|
+
throwOnReceiptRevert,
|
|
713
|
+
} as never)
|
|
714
|
+
// Pending and (with `throwOnReceiptRevert: false`) reverted receipts carry no upgrade event.
|
|
715
|
+
if ((receipt as Transaction.TransactionReceipt).status !== 'success')
|
|
716
|
+
return { receipt } as never
|
|
717
|
+
const { args } = upgrade.extractEvent(receipt.logs)
|
|
718
|
+
return {
|
|
719
|
+
account: args.account,
|
|
720
|
+
commitment: args.commitment,
|
|
721
|
+
config: AccountConfig.from({
|
|
722
|
+
owners: args.owners,
|
|
723
|
+
salt: args.salt,
|
|
724
|
+
threshold: args.threshold,
|
|
725
|
+
version: 1n,
|
|
726
|
+
}),
|
|
727
|
+
receipt,
|
|
728
|
+
} as never
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export namespace upgradeSync {
|
|
732
|
+
export type Parameters<
|
|
733
|
+
chain extends Chain | undefined = Chain | undefined,
|
|
734
|
+
account extends Account | undefined = Account | undefined,
|
|
735
|
+
> = upgrade.Parameters<chain, account>
|
|
736
|
+
|
|
737
|
+
export type Args = upgrade.Args
|
|
738
|
+
|
|
739
|
+
/** Pending and reverted receipts carry no installed configuration. */
|
|
740
|
+
export type ReturnValue = OneOf<
|
|
741
|
+
| {
|
|
742
|
+
account: Address
|
|
743
|
+
commitment: Hex.Hex
|
|
744
|
+
config: AccountConfig.Config
|
|
745
|
+
receipt: Transaction.TransactionReceipt
|
|
746
|
+
}
|
|
747
|
+
| { receipt: Transaction.TransactionReceipt }
|
|
748
|
+
>
|
|
749
|
+
|
|
750
|
+
export type ErrorType = upgrade.ErrorType
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Verifies a configurable account signature against the account's current
|
|
755
|
+
* configuration with the signature verifier precompile.
|
|
756
|
+
*
|
|
757
|
+
* Returns `false` when the signature is for another account or a config that
|
|
758
|
+
* is not the account's current config. Throws when the signature is malformed
|
|
759
|
+
* or its approvals do not reach quorum.
|
|
760
|
+
*
|
|
761
|
+
* @example
|
|
762
|
+
* ```ts
|
|
763
|
+
* import { createClient, http } from 'viem'
|
|
764
|
+
* import { tempo } from 'viem/chains'
|
|
765
|
+
* import { Actions } from 'viem/tempo'
|
|
766
|
+
*
|
|
767
|
+
* const client = createClient({
|
|
768
|
+
* chain: tempo,
|
|
769
|
+
* transport: http(),
|
|
770
|
+
* })
|
|
771
|
+
*
|
|
772
|
+
* const valid = await Actions.accounts.verifyHash(client, {
|
|
773
|
+
* account: '0x...',
|
|
774
|
+
* hash: '0x...',
|
|
775
|
+
* signature: '0x...',
|
|
776
|
+
* })
|
|
777
|
+
* ```
|
|
778
|
+
*
|
|
779
|
+
* @param client - Client.
|
|
780
|
+
* @param parameters - Parameters.
|
|
781
|
+
* @returns Whether the signature is valid for the account.
|
|
782
|
+
*/
|
|
783
|
+
export async function verifyHash<
|
|
784
|
+
chain extends Chain | undefined,
|
|
785
|
+
account extends Account | undefined,
|
|
786
|
+
>(
|
|
787
|
+
client: Client<Transport, chain, account>,
|
|
788
|
+
parameters: verifyHash.Parameters,
|
|
789
|
+
): Promise<verifyHash.ReturnValue> {
|
|
790
|
+
const { account, hash, signature, ...rest } = parameters
|
|
791
|
+
return withExecutionError(readContract)(client, {
|
|
792
|
+
...rest,
|
|
793
|
+
...verifyHash.call({ account, hash, signature }),
|
|
794
|
+
})
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
export namespace verifyHash {
|
|
798
|
+
export type Parameters = ReadParameters & Args
|
|
799
|
+
|
|
800
|
+
export type Args = {
|
|
801
|
+
/** Configurable account address the signature is expected to belong to. */
|
|
802
|
+
account: Address
|
|
803
|
+
/** Original message hash that was signed. */
|
|
804
|
+
hash: Hex.Hex
|
|
805
|
+
/** Configurable account signature envelope. */
|
|
806
|
+
signature: Hex.Hex
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export type ReturnValue = ReadContractReturnType<
|
|
810
|
+
typeof Abis.signatureVerifier,
|
|
811
|
+
'verifyConfigurableAccount',
|
|
812
|
+
never
|
|
813
|
+
>
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Defines a call to the `verifyConfigurableAccount` function on the
|
|
817
|
+
* signature verifier precompile.
|
|
818
|
+
*
|
|
819
|
+
* @param args - Arguments.
|
|
820
|
+
* @returns The call.
|
|
821
|
+
*/
|
|
822
|
+
export function call(args: Args) {
|
|
823
|
+
const { account, hash, signature } = args
|
|
824
|
+
return defineCall({
|
|
825
|
+
address: Addresses.signatureVerifier,
|
|
826
|
+
abi: Abis.signatureVerifier,
|
|
827
|
+
args: [account, hash, signature],
|
|
828
|
+
functionName: 'verifyConfigurableAccount',
|
|
829
|
+
})
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
export type ErrorType = Errors.ExecutionErrorType | BaseErrorType
|
|
833
|
+
}
|
package/tempo/chainConfig.ts
CHANGED
|
@@ -9,8 +9,11 @@ import {
|
|
|
9
9
|
} from 'ox/tempo'
|
|
10
10
|
import { getCode } from '../actions/public/getCode.js'
|
|
11
11
|
import { getTransaction } from '../actions/public/getTransaction.js'
|
|
12
|
+
import { readContract } from '../actions/public/readContract.js'
|
|
12
13
|
import { verifyHash } from '../actions/public/verifyHash.js'
|
|
13
14
|
import { maxUint256 } from '../constants/number.js'
|
|
15
|
+
import { BaseError } from '../errors/base.js'
|
|
16
|
+
import { ContractFunctionRevertedError } from '../errors/contract.js'
|
|
14
17
|
import type { Chain, ChainConfig as viem_ChainConfig } from '../types/chain.js'
|
|
15
18
|
import { isAddressEqual } from '../utils/address/isAddressEqual.js'
|
|
16
19
|
import { extendSchema } from '../utils/chain/defineChain.js'
|
|
@@ -22,7 +25,10 @@ import { keccak256 } from '../utils/hash/keccak256.js'
|
|
|
22
25
|
import type { SerializeTransactionFn } from '../utils/transaction/serializeTransaction.js'
|
|
23
26
|
import type { Account, ConfigurableAccount } from './Account.js'
|
|
24
27
|
import { getMetadata } from './actions/accessKey.js'
|
|
25
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
getConfig,
|
|
30
|
+
verifyHash as verifyConfigurableAccount,
|
|
31
|
+
} from './actions/accounts.js'
|
|
26
32
|
import * as Formatters from './Formatters.js'
|
|
27
33
|
import type { Hardfork } from './Hardfork.js'
|
|
28
34
|
import * as Concurrent from './internal/concurrent.js'
|
|
@@ -318,10 +324,15 @@ export const chainConfig = {
|
|
|
318
324
|
async verifyHash(client, parameters) {
|
|
319
325
|
const { address, hash, signature, mode } = parameters
|
|
320
326
|
|
|
327
|
+
const serialized = (() => {
|
|
328
|
+
if (typeof signature === 'string') return signature
|
|
329
|
+
if (signature instanceof Uint8Array) return Hex.fromBytes(signature)
|
|
330
|
+
return undefined
|
|
331
|
+
})()
|
|
321
332
|
const envelope = (() => {
|
|
322
|
-
if (
|
|
333
|
+
if (!serialized) return
|
|
323
334
|
try {
|
|
324
|
-
return SignatureEnvelope.deserialize(
|
|
335
|
+
return SignatureEnvelope.deserialize(serialized)
|
|
325
336
|
} catch {
|
|
326
337
|
return undefined
|
|
327
338
|
}
|
|
@@ -370,6 +381,33 @@ export const chainConfig = {
|
|
|
370
381
|
})
|
|
371
382
|
}
|
|
372
383
|
|
|
384
|
+
// Configurable account signatures are verified against the account's
|
|
385
|
+
// current config commitment, which only the chain knows.
|
|
386
|
+
// Reverts (malformed signatures or approvals below quorum) are invalid signatures.
|
|
387
|
+
if (envelope.type === 'configurable')
|
|
388
|
+
return await (
|
|
389
|
+
readContract(client, {
|
|
390
|
+
blockHash: parameters.blockHash,
|
|
391
|
+
blockNumber: parameters.blockNumber,
|
|
392
|
+
blockTag: parameters.blockTag,
|
|
393
|
+
requireCanonical: parameters.requireCanonical,
|
|
394
|
+
...verifyConfigurableAccount.call({
|
|
395
|
+
account: address,
|
|
396
|
+
hash,
|
|
397
|
+
signature: serialized as Hex.Hex,
|
|
398
|
+
}),
|
|
399
|
+
} as never) as Promise<boolean>
|
|
400
|
+
).catch((error) => {
|
|
401
|
+
if (
|
|
402
|
+
error instanceof BaseError &&
|
|
403
|
+
error.walk(
|
|
404
|
+
(cause) => cause instanceof ContractFunctionRevertedError,
|
|
405
|
+
)
|
|
406
|
+
)
|
|
407
|
+
return false
|
|
408
|
+
throw error
|
|
409
|
+
})
|
|
410
|
+
|
|
373
411
|
// Stateless, non-keychain signature envelopes (P256, WebAuthn) can be
|
|
374
412
|
// verified directly without a network request.
|
|
375
413
|
if (envelope.type === 'p256' || envelope.type === 'webAuthn') {
|
|
@@ -225,8 +225,16 @@ async function submit(options: submit.Options) {
|
|
|
225
225
|
if (
|
|
226
226
|
options.method === 'eth_sendRawTransaction' ||
|
|
227
227
|
options.method === 'eth_sendRawTransactionSync'
|
|
228
|
-
)
|
|
229
|
-
|
|
228
|
+
) {
|
|
229
|
+
const result = await options.next(options.request, options.requestOptions)
|
|
230
|
+
// Cache after the node accepts the transaction, so only validly signed upgrades are cached.
|
|
231
|
+
// Caching is best-effort: a store failure must not mask an accepted upgrade.
|
|
232
|
+
if (transaction)
|
|
233
|
+
await cacheUpgradeConfig({ store: options.store, transaction }).catch(
|
|
234
|
+
() => {},
|
|
235
|
+
)
|
|
236
|
+
return result
|
|
237
|
+
}
|
|
230
238
|
throw new RpcResponse.InvalidParamsError({
|
|
231
239
|
message: 'Expected a configurable account transaction signature.',
|
|
232
240
|
})
|
|
@@ -758,7 +766,7 @@ function assertConfig(options: {
|
|
|
758
766
|
if (
|
|
759
767
|
config.version === 0n &&
|
|
760
768
|
AccountConfig.getAddress(config, {
|
|
761
|
-
factory: Addresses.
|
|
769
|
+
factory: Addresses.configurableAccountRecoveryFactory,
|
|
762
770
|
}).toLowerCase() !== account.toLowerCase()
|
|
763
771
|
)
|
|
764
772
|
throw new RpcResponse.InvalidParamsError({
|
|
@@ -798,13 +806,16 @@ async function cacheNextConfigs(options: cacheNextConfigs.Options) {
|
|
|
798
806
|
for (const call of options.transaction.calls) {
|
|
799
807
|
if (
|
|
800
808
|
!call.to ||
|
|
801
|
-
!isAddressEqual(call.to, Addresses.
|
|
809
|
+
!isAddressEqual(call.to, Addresses.configurableAccount) ||
|
|
802
810
|
!call.data
|
|
803
811
|
)
|
|
804
812
|
continue
|
|
805
813
|
const decoded = (() => {
|
|
806
814
|
try {
|
|
807
|
-
return decodeFunctionData({
|
|
815
|
+
return decodeFunctionData({
|
|
816
|
+
abi: Abis.configurableAccount,
|
|
817
|
+
data: call.data,
|
|
818
|
+
})
|
|
808
819
|
} catch {
|
|
809
820
|
return undefined
|
|
810
821
|
}
|
|
@@ -855,6 +866,52 @@ declare namespace cacheNextConfigs {
|
|
|
855
866
|
}
|
|
856
867
|
}
|
|
857
868
|
|
|
869
|
+
/** Caches the config installed by a root-signed account upgrade. */
|
|
870
|
+
// biome-ignore lint/correctness/noUnusedVariables: declaration merge
|
|
871
|
+
async function cacheUpgradeConfig(options: cacheUpgradeConfig.Options) {
|
|
872
|
+
const { calls, from, signature } = options.transaction
|
|
873
|
+
const [call] = calls
|
|
874
|
+
if (
|
|
875
|
+
!from ||
|
|
876
|
+
!signature ||
|
|
877
|
+
signature.type === 'keychain' ||
|
|
878
|
+
calls.length !== 1 ||
|
|
879
|
+
!call?.to ||
|
|
880
|
+
!isAddressEqual(call.to, Addresses.configurableAccount) ||
|
|
881
|
+
!call.data
|
|
882
|
+
)
|
|
883
|
+
return
|
|
884
|
+
const config = (() => {
|
|
885
|
+
try {
|
|
886
|
+
const decoded = decodeFunctionData({
|
|
887
|
+
abi: Abis.configurableAccount,
|
|
888
|
+
data: call.data,
|
|
889
|
+
})
|
|
890
|
+
if (decoded.functionName !== 'upgradeAccount') return undefined
|
|
891
|
+
const [threshold, owners] = decoded.args
|
|
892
|
+
return AccountConfig.fromUpgrade(from, { owners, threshold })
|
|
893
|
+
} catch {
|
|
894
|
+
return undefined
|
|
895
|
+
}
|
|
896
|
+
})()
|
|
897
|
+
if (!config) return
|
|
898
|
+
await ConfigStore.write(options.store, {
|
|
899
|
+
address: from,
|
|
900
|
+
commitment: AccountConfig.getCommitment(config),
|
|
901
|
+
config,
|
|
902
|
+
})
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
declare namespace cacheUpgradeConfig {
|
|
906
|
+
/** Parameters for {@link cacheUpgradeConfig}. */
|
|
907
|
+
export type Options = {
|
|
908
|
+
/** Shared account store. */
|
|
909
|
+
store: Store.Store
|
|
910
|
+
/** Submitted transaction containing a potential account upgrade. */
|
|
911
|
+
transaction: Transaction.TransactionSerializableTempo
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
858
915
|
/** Validates primitive approvals and translates invalid input into an RPC error. @internal */
|
|
859
916
|
export async function selectApprovals(
|
|
860
917
|
options: AccountOperation.selectApprovals.Options,
|
|
@@ -174,8 +174,8 @@ export function create(options: Relay.feePayer.Options): Relay.Plugin {
|
|
|
174
174
|
Object.assign(filled, { feeToken: token })
|
|
175
175
|
|
|
176
176
|
const defer =
|
|
177
|
-
typeof normalized.
|
|
178
|
-
normalized.
|
|
177
|
+
typeof normalized.configurableAccountSimulation === 'object' &&
|
|
178
|
+
normalized.configurableAccountSimulation !== null
|
|
179
179
|
|
|
180
180
|
// Bound before validation so the validated envelope is the one signed, and returned signatures expire.
|
|
181
181
|
const signs = !!options.account && !external && !defer
|
|
@@ -237,8 +237,8 @@ export function create(options: Relay.feePayer.Options): Relay.Plugin {
|
|
|
237
237
|
typeof parameters.feePayer === 'string' ||
|
|
238
238
|
parameters.feePayer === false ||
|
|
239
239
|
!result.capabilities?.sponsored ||
|
|
240
|
-
(typeof parameters.
|
|
241
|
-
parameters.
|
|
240
|
+
(typeof parameters.configurableAccountSimulation === 'object' &&
|
|
241
|
+
parameters.configurableAccountSimulation !== null)
|
|
242
242
|
)
|
|
243
243
|
return undefined
|
|
244
244
|
const transaction = Utils.normalizeTempoTransaction(result.tx)
|
|
@@ -39,6 +39,10 @@ export function withRetry<data>(
|
|
|
39
39
|
}: WithRetryParameters = {},
|
|
40
40
|
) {
|
|
41
41
|
return new Promise<data>((resolve, reject) => {
|
|
42
|
+
// Settles on abort even when `fn` ignores the signal.
|
|
43
|
+
const onAbort = () => reject(getAbortError(signal))
|
|
44
|
+
signal?.addEventListener('abort', onAbort, { once: true })
|
|
45
|
+
|
|
42
46
|
const attemptRetry = async ({ count = 0 } = {}): Promise<void> => {
|
|
43
47
|
if (signal?.aborted) {
|
|
44
48
|
reject(getAbortError(signal))
|
|
@@ -79,6 +83,8 @@ export function withRetry<data>(
|
|
|
79
83
|
reject(err)
|
|
80
84
|
}
|
|
81
85
|
}
|
|
82
|
-
void attemptRetry()
|
|
86
|
+
void attemptRetry()
|
|
87
|
+
.catch(reject)
|
|
88
|
+
.finally(() => signal?.removeEventListener('abort', onAbort))
|
|
83
89
|
})
|
|
84
90
|
}
|