Skip to content

Commit aa8645c

Browse files
committed
feat(tempo): add experimental Account.fromAttested and attested verifyHash
Adds `Account.fromAttested({ policyId, userTag, sign })`, whose signatures come from a remote signer program as attested (`0x06`) signatures, for transactions, key authorizations, configurable account owner approvals, messages, and typed data. Attested accounts can approve configurable account transactions, take the conservative gas estimation hint, and `verifyHash` checks attested signatures through the signature verifier precompile (TIP-1020). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D3nmWhznBuzqxnKfa9brpB
1 parent a67748e commit aa8645c

10 files changed

Lines changed: 654 additions & 9 deletions

File tree

‎.changeset/tempo-attested-signers.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"viem": patch
33
---
44

5-
Added experimental `Actions.attested` actions, `Abis.attestedKeyRegistry`, `Selectors.attestedKeyRegistry`, and `Addresses.attestedKeyRegistry` to `viem/tempo` for the Attested Key Registry of attested signers (TIP-1159 to TIP-1163).
5+
Added experimental attested signer support to `viem/tempo`: `Account.fromAttested`, attested (`0x06`) owner approvals and `verifyHash`, `Actions.attested` actions, `Abis.attestedKeyRegistry`, `Selectors.attestedKeyRegistry`, and `Addresses.attestedKeyRegistry` (TIP-1159 to TIP-1163).
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# `Account.fromAttested`
2+
3+
Instantiates an attested signer account ([TIP-1159](https://github.com/tempoxyz/tempo/pull/8822)). Its address derives from a policy ID and a user tag. A signer program in a trusted execution environment signs for it with a program key that the Attested Key Registry holds as current.
4+
5+
:::warning
6+
Experimental. Attested signers are a draft and are not active on any public Tempo network.
7+
:::
8+
9+
## Usage
10+
11+
The account delegates every signature to `sign`. It passes the context digest, such as a transaction's signing hash, and expects the signer program's serialized attested (`0x06`) signature.
12+
13+
```ts twoslash
14+
import { Account } from 'viem/tempo'
15+
16+
const account = Account.fromAttested({
17+
policyId: '0xadbe28853696826a0cd6f2dcc61811025e4d954b6be3912b44aa51ae65a0339c',
18+
userTag: '0x3333333333333333333333333333333333333333333333333333333333333333',
19+
async sign({ hash }) {
20+
const response = await fetch('https://signer.example/sign', {
21+
body: JSON.stringify({ hash }),
22+
method: 'POST',
23+
})
24+
const { signature } = await response.json()
25+
return signature
26+
},
27+
})
28+
29+
console.log('Address:', account.address)
30+
// @log: Address: 0xc9c72810362348E09b52987695042F894D9f5352
31+
```
32+
33+
The account checks that each signature verifies for its address and the digest. The node also checks the Attested Key Registry, which a local check cannot read.
34+
35+
### Configurable Account Owner
36+
37+
An attested signer can own a [configurable account](/tempo/accounts/account.fromConfig). Its approvals sign the owner approval digest, as other owners do.
38+
39+
```ts twoslash
40+
import { Account } from 'viem/tempo'
41+
42+
declare const attested: Account.AttestedAccount
43+
// ---cut---
44+
const account = Account.fromConfig({
45+
owners: [attested], // [!code focus]
46+
})
47+
```
48+
49+
## Return Type
50+
51+
```ts
52+
type ReturnType = AttestedAccount
53+
54+
type AttestedAccount = {
55+
/** Address derived from the policy ID and the user tag. */
56+
address: Address
57+
/** Key type. */
58+
keyType: 'attested'
59+
/** Attested Key Registry policy ID. */
60+
policyId: Hex
61+
/** Always `0x`: the program key is in each signature. */
62+
publicKey: Hex
63+
/** Account source. */
64+
source: 'attested'
65+
/** Account type. */
66+
type: 'local'
67+
/** Tag of the user whose address the account signs for. */
68+
userTag: Hex
69+
/** Signs a hash. Returns a serialized attested signature. */
70+
sign: (parameters: { hash: Hex }) => Promise<Hex>
71+
/** Signs a key authorization as the account. */
72+
signKeyAuthorization: (key, parameters) => Promise<KeyAuthorization.Signed>
73+
/** Signs an EIP-191 message. */
74+
signMessage: (parameters: { message: SignableMessage }) => Promise<Hex>
75+
/** Signs a transaction, or a configurable account owner approval. */
76+
signTransaction: (transaction, options?) => Promise<Hex>
77+
/** Signs EIP-712 typed data. */
78+
signTypedData: (typedData) => Promise<Hex>
79+
}
80+
```
81+
82+
## Parameters
83+
84+
### policyId
85+
86+
- **Type:** `Hex`
87+
88+
Attested Key Registry policy ID. See [`attested.createPolicy`](/tempo/actions/attested.createPolicy).
89+
90+
### sign
91+
92+
- **Type:** `(parameters: { hash: Hex }) => Promise<Hex>`
93+
94+
Requests the signer program's attested signature over a context digest, and returns the serialized `0x06` signature.
95+
96+
### userTag
97+
98+
- **Type:** `Hex`
99+
100+
Tag of the user whose address the account signs for. The signer program derives it from the user's identity.

‎site/vocs.config.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2589,6 +2589,11 @@ export default defineConfig({
25892589
text: 'WebCryptoP256',
25902590
link: '/tempo/accounts/account.fromWebCryptoP256',
25912591
},
2592+
{
2593+
badge: { text: 'EXP', variant: 'warning' },
2594+
text: 'Attested Signer',
2595+
link: '/tempo/accounts/account.fromAttested',
2596+
},
25922597
{
25932598
badge: { text: 'EXP', variant: 'warning' },
25942599
text: 'Configurable',
@@ -2750,6 +2755,7 @@ export default defineConfig({
27502755
],
27512756
},
27522757
{
2758+
badge: { text: 'EXP', variant: 'warning' },
27532759
text: 'Attested Signers',
27542760
collapsed: true,
27552761
items: [

‎src/tempo/Account.test-d.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ const owner = Account.fromSecp256k1(
66
'0x0000000000000000000000000000000000000000000000000000000000000001',
77
)
88

9+
test('fromAttested', () => {
10+
const attested = Account.fromAttested({
11+
policyId: '0x',
12+
sign: async () => '0x',
13+
userTag: '0x',
14+
})
15+
16+
expectTypeOf(attested).toEqualTypeOf<Account.AttestedAccount>()
17+
expectTypeOf(attested.source).toEqualTypeOf<'attested'>()
18+
expectTypeOf(Account.fromConfig({ owners: [attested] })).toEqualTypeOf<
19+
Account.ConfigurableAccount<AccountConfig.Config>
20+
>()
21+
})
22+
923
test('fromConfig preserves config availability', () => {
1024
const config = {
1125
owners: [owner],

0 commit comments

Comments
 (0)