ethagent 3.2.0 → 3.3.1
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/README.md +15 -16
- package/package.json +1 -1
- package/src/identity/continuity/history.ts +8 -8
- package/src/identity/continuity/publicSkills.ts +2 -79
- package/src/identity/continuity/skills/publicSkillsSync.ts +8 -7
- package/src/identity/continuity/snapshots.ts +3 -8
- package/src/identity/continuity/storage/defaults.ts +3 -3
- package/src/identity/continuity/storage/paths.ts +1 -1
- package/src/identity/continuity/storage/scaffold.ts +37 -25
- package/src/identity/continuity/storage/status.ts +11 -11
- package/src/identity/continuity/storage/types.ts +4 -4
- package/src/identity/continuity/storage.ts +4 -4
- package/src/identity/ens/agentRecords.ts +61 -45
- package/src/identity/ens/ensAutomation/read.ts +7 -10
- package/src/identity/ens/ensAutomation/setup.ts +10 -16
- package/src/identity/ens/ensAutomation/types.ts +0 -1
- package/src/identity/ens/ensAutomation.ts +1 -0
- package/src/identity/ens/ensLookup/records.ts +1 -1
- package/src/identity/ens/ensLookup.ts +1 -1
- package/src/identity/ens/erc7930.ts +48 -0
- package/src/identity/hub/OperationalRoutes.tsx +4 -2
- package/src/identity/hub/Routes.tsx +1 -1
- package/src/identity/hub/continuity/effects.ts +17 -39
- package/src/identity/hub/continuity/skills/NewSkillVisibilityScreen.tsx +2 -2
- package/src/identity/hub/continuity/skills/SkillActionsScreen.tsx +2 -2
- package/src/identity/hub/continuity/state.ts +1 -1
- package/src/identity/hub/continuity/vault.ts +16 -50
- package/src/identity/hub/create/effects.ts +12 -16
- package/src/identity/hub/ens/EnsEditAdvancedScreens.tsx +0 -76
- package/src/identity/hub/ens/EnsEditFlow.tsx +28 -8
- package/src/identity/hub/ens/EnsEditMaintenanceScreens.tsx +3 -7
- package/src/identity/hub/ens/EnsEditReviewScreens.tsx +14 -18
- package/src/identity/hub/ens/EnsEditShared.tsx +4 -6
- package/src/identity/hub/ens/EnsEditSimpleScreens.tsx +2 -2
- package/src/identity/hub/ens/EnsFlow.tsx +0 -3
- package/src/identity/hub/ens/editCopy.ts +7 -15
- package/src/identity/hub/ens/transactions.ts +67 -18
- package/src/identity/hub/ens/types.ts +0 -2
- package/src/identity/hub/profile/EditProfileFlow.tsx +0 -3
- package/src/identity/hub/profile/effects.ts +15 -30
- package/src/identity/hub/profile/identity.ts +2 -4
- package/src/identity/hub/profile/operatorSave.ts +10 -30
- package/src/identity/hub/restore/RestoreFlow.tsx +9 -9
- package/src/identity/hub/restore/apply.ts +7 -8
- package/src/identity/hub/restore/helpers.ts +3 -3
- package/src/identity/hub/restore/recovery.ts +9 -10
- package/src/identity/hub/restore/resolve.ts +11 -9
- package/src/identity/hub/shared/components/MenuScreen.tsx +3 -3
- package/src/identity/hub/shared/components/UnlinkedIdentityScreen.tsx +2 -2
- package/src/identity/hub/shared/effects/sync.ts +1 -1
- package/src/identity/hub/transfer/TokenTransferScreens.tsx +1 -1
- package/src/identity/hub/transfer/effects.ts +10 -31
- package/src/identity/hub/useIdentityHubContinuity.ts +12 -12
- package/src/identity/hub/useIdentityHubController.ts +12 -3
- package/src/identity/registry/erc8004/metadata.ts +10 -27
- package/src/identity/registry/erc8004/types.ts +0 -1
- package/src/storage/config.ts +1 -2
- package/src/storage/identity.ts +3 -3
- package/src/storage/rewind.ts +1 -1
- package/src/tools/privateContinuityEditTool.ts +4 -4
- package/src/utils/withRetry.ts +2 -2
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
prepareSyncedIdentityMarkdownScaffold,
|
|
5
5
|
prepareSyncedSkillsTree,
|
|
6
6
|
readContinuityFiles,
|
|
7
|
-
readPublicSkillsFile,
|
|
8
7
|
writeIdentityMarkdownScaffold,
|
|
9
8
|
type IdentityMarkdownScaffold,
|
|
10
9
|
} from '../../continuity/storage.js'
|
|
@@ -16,14 +15,7 @@ import {
|
|
|
16
15
|
type WalletChallengePurpose,
|
|
17
16
|
} from '../../continuity/envelope.js'
|
|
18
17
|
import {
|
|
19
|
-
|
|
20
|
-
createAgentCard,
|
|
21
|
-
defaultPublicSkillsProfile,
|
|
22
|
-
serializeAgentCard,
|
|
23
|
-
} from '../../continuity/publicSkills.js'
|
|
24
|
-
import {
|
|
25
|
-
derivePublicSkillEntries,
|
|
26
|
-
syncPublicSkillsManifest,
|
|
18
|
+
syncAgentCardManifest,
|
|
27
19
|
} from '../../continuity/skills/publicSkillsSync.js'
|
|
28
20
|
import { recordPublishedContinuitySnapshot } from '../../continuity/snapshots.js'
|
|
29
21
|
import { addToIpfs, DEFAULT_IPFS_API_URL } from '../../storage/ipfs.js'
|
|
@@ -60,7 +52,7 @@ import {
|
|
|
60
52
|
import { markCurrentContinuityFilesPublished } from '../shared/effects/sync.js'
|
|
61
53
|
|
|
62
54
|
type BackupMetadata = NonNullable<EthagentIdentity['backup']>
|
|
63
|
-
type
|
|
55
|
+
type AgentCardMetadata = NonNullable<EthagentIdentity['agentCard']>
|
|
64
56
|
|
|
65
57
|
type VaultPublishPrepared = {
|
|
66
58
|
nextIdentity: EthagentIdentity
|
|
@@ -68,7 +60,7 @@ type VaultPublishPrepared = {
|
|
|
68
60
|
completionMessage: string
|
|
69
61
|
publishedSources: {
|
|
70
62
|
privateFiles: ContinuityFiles
|
|
71
|
-
|
|
63
|
+
agentCard: string
|
|
72
64
|
skills: ContinuitySkillsTree
|
|
73
65
|
}
|
|
74
66
|
}
|
|
@@ -142,20 +134,8 @@ export async function runOperatorWalletRebackup(args: {
|
|
|
142
134
|
const continuityFiles = markdownScaffold
|
|
143
135
|
? { 'SOUL.md': markdownScaffold['SOUL.md'], 'MEMORY.md': markdownScaffold['MEMORY.md'] }
|
|
144
136
|
: await readContinuityFiles(nextIdentityForFiles)
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
assertVerifiedPin(publicSkillsPin)
|
|
148
|
-
const publicSkillEntries = await derivePublicSkillEntries(nextIdentityForFiles)
|
|
149
|
-
const augmentedPublicProfile = appendPublicSkillEntries(
|
|
150
|
-
defaultPublicSkillsProfile(nextIdentityForFiles),
|
|
151
|
-
publicSkillEntries,
|
|
152
|
-
)
|
|
153
|
-
const agentCardPin = await addToIpfs(
|
|
154
|
-
DEFAULT_IPFS_API_URL,
|
|
155
|
-
serializeAgentCard(createAgentCard(augmentedPublicProfile)),
|
|
156
|
-
fetch,
|
|
157
|
-
{ pinataJwt: step.pinataJwt },
|
|
158
|
-
)
|
|
137
|
+
const agentCardJson = await syncAgentCardManifest(nextIdentityForFiles)
|
|
138
|
+
const agentCardPin = await addToIpfs(DEFAULT_IPFS_API_URL, agentCardJson, fetch, { pinataJwt: step.pinataJwt })
|
|
159
139
|
assertVerifiedPin(agentCardPin)
|
|
160
140
|
const skillsTree = await prepareSyncedSkillsTree(nextIdentityForFiles)
|
|
161
141
|
const envelope = createContinuityEnvelopeForSave({
|
|
@@ -185,9 +165,8 @@ export async function runOperatorWalletRebackup(args: {
|
|
|
185
165
|
identityRegistryAddress: step.registry.identityRegistryAddress,
|
|
186
166
|
agentId: sourceAgentId,
|
|
187
167
|
}
|
|
188
|
-
const
|
|
189
|
-
cid:
|
|
190
|
-
agentCardCid: agentCardPin.cid,
|
|
168
|
+
const agentCard: AgentCardMetadata = {
|
|
169
|
+
cid: agentCardPin.cid,
|
|
191
170
|
updatedAt: envelope.createdAt,
|
|
192
171
|
status: 'pinned',
|
|
193
172
|
}
|
|
@@ -196,7 +175,7 @@ export async function runOperatorWalletRebackup(args: {
|
|
|
196
175
|
...step.identity,
|
|
197
176
|
state,
|
|
198
177
|
backup,
|
|
199
|
-
|
|
178
|
+
agentCard,
|
|
200
179
|
}
|
|
201
180
|
|
|
202
181
|
if (markdownScaffold) {
|
|
@@ -205,7 +184,7 @@ export async function runOperatorWalletRebackup(args: {
|
|
|
205
184
|
await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'local operator-wallet snapshot' }).catch(() => null)
|
|
206
185
|
await markCurrentContinuityFilesPublished(nextIdentity, {
|
|
207
186
|
privateFiles: continuityFiles,
|
|
208
|
-
|
|
187
|
+
agentCard: agentCardJson,
|
|
209
188
|
skills: skillsTree,
|
|
210
189
|
}).catch(() => null)
|
|
211
190
|
const completionMessage = nextEnsName !== undefined && nextEnsName !== ((step.identity.state as Record<string, unknown> | undefined)?.ensName as string | undefined)
|
|
@@ -284,20 +263,8 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
284
263
|
const continuityFiles = markdownScaffold
|
|
285
264
|
? { 'SOUL.md': markdownScaffold['SOUL.md'], 'MEMORY.md': markdownScaffold['MEMORY.md'] }
|
|
286
265
|
: await readContinuityFiles(nextIdentityForFiles)
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
assertVerifiedPin(publicSkillsPin)
|
|
290
|
-
const publicSkillEntries = await derivePublicSkillEntries(nextIdentityForFiles)
|
|
291
|
-
const augmentedPublicProfile = appendPublicSkillEntries(
|
|
292
|
-
defaultPublicSkillsProfile(nextIdentityForFiles),
|
|
293
|
-
publicSkillEntries,
|
|
294
|
-
)
|
|
295
|
-
const agentCardPin = await addToIpfs(
|
|
296
|
-
DEFAULT_IPFS_API_URL,
|
|
297
|
-
serializeAgentCard(createAgentCard(augmentedPublicProfile)),
|
|
298
|
-
fetch,
|
|
299
|
-
{ pinataJwt: step.pinataJwt },
|
|
300
|
-
)
|
|
266
|
+
const agentCardJson = await syncAgentCardManifest(nextIdentityForFiles)
|
|
267
|
+
const agentCardPin = await addToIpfs(DEFAULT_IPFS_API_URL, agentCardJson, fetch, { pinataJwt: step.pinataJwt })
|
|
301
268
|
assertVerifiedPin(agentCardPin)
|
|
302
269
|
const skillsTree = await prepareSyncedSkillsTree(nextIdentityForFiles)
|
|
303
270
|
const envelope = createContinuityEnvelopeForSave({
|
|
@@ -315,9 +282,8 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
315
282
|
const statePin = await addToIpfs(DEFAULT_IPFS_API_URL, serializeContinuitySnapshotEnvelope(envelope), fetch, { pinataJwt: step.pinataJwt })
|
|
316
283
|
assertVerifiedPin(statePin)
|
|
317
284
|
|
|
318
|
-
const
|
|
319
|
-
cid:
|
|
320
|
-
agentCardCid: agentCardPin.cid,
|
|
285
|
+
const agentCard: AgentCardMetadata = {
|
|
286
|
+
cid: agentCardPin.cid,
|
|
321
287
|
updatedAt: envelope.createdAt,
|
|
322
288
|
status: 'pinned',
|
|
323
289
|
}
|
|
@@ -329,7 +295,7 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
329
295
|
...(uploadedImageUri ? { image: uploadedImageUri } : {}),
|
|
330
296
|
}, {
|
|
331
297
|
backup: { cid: statePin.cid, envelopeVersion: envelope.envelopeVersion, createdAt: envelope.createdAt },
|
|
332
|
-
publicDiscovery: {
|
|
298
|
+
publicDiscovery: { agentCardCid: agentCard.cid, updatedAt: agentCard.updatedAt },
|
|
333
299
|
registration: { chainId: step.registry.chainId, identityRegistryAddress: step.registry.identityRegistryAddress, agentId: sourceAgentId },
|
|
334
300
|
ensName: nextEnsName,
|
|
335
301
|
operators: operatorsPointerFromState(state, nextEnsName),
|
|
@@ -365,7 +331,7 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
365
331
|
...step.identity,
|
|
366
332
|
state,
|
|
367
333
|
backup,
|
|
368
|
-
|
|
334
|
+
agentCard,
|
|
369
335
|
agentUri,
|
|
370
336
|
metadataCid,
|
|
371
337
|
}
|
|
@@ -383,7 +349,7 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
383
349
|
completionMessage,
|
|
384
350
|
publishedSources: {
|
|
385
351
|
privateFiles: continuityFiles,
|
|
386
|
-
|
|
352
|
+
agentCard: agentCardJson,
|
|
387
353
|
skills: skillsTree,
|
|
388
354
|
},
|
|
389
355
|
},
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
createAgentCard,
|
|
16
16
|
defaultPublicSkillsProfile,
|
|
17
|
-
renderPublicSkillsJson,
|
|
18
17
|
serializeAgentCard,
|
|
19
18
|
} from '../../continuity/publicSkills.js'
|
|
20
19
|
import { recordPublishedContinuitySnapshot } from '../../continuity/snapshots.js'
|
|
@@ -43,17 +42,17 @@ import { awaitConfirmedReceipt } from '../shared/effects/receipts.js'
|
|
|
43
42
|
import { assertVerifiedPin } from '../shared/effects/profilePrep.js'
|
|
44
43
|
|
|
45
44
|
type BackupMetadata = NonNullable<EthagentIdentity['backup']>
|
|
46
|
-
type
|
|
45
|
+
type AgentCardMetadata = NonNullable<EthagentIdentity['agentCard']>
|
|
47
46
|
|
|
48
47
|
type CreatePreparedTransaction = {
|
|
49
48
|
ownerAddress: Address
|
|
50
49
|
agentUri: string
|
|
51
50
|
metadataCid: string
|
|
52
51
|
backup: BackupMetadata
|
|
53
|
-
|
|
52
|
+
agentCard: AgentCardMetadata
|
|
54
53
|
state: Record<string, unknown>
|
|
55
54
|
continuityFiles: ReturnType<typeof defaultContinuityFiles>
|
|
56
|
-
|
|
55
|
+
agentCardJson: string
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
export async function runCreatePreflight(
|
|
@@ -139,10 +138,8 @@ export async function runCreateSigning(
|
|
|
139
138
|
})
|
|
140
139
|
const continuityFiles = defaultContinuityFiles(draftIdentity)
|
|
141
140
|
const publicProfile = defaultPublicSkillsProfile(draftIdentity)
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
assertVerifiedPin(publicSkillsPin)
|
|
145
|
-
const agentCardPin = await addToIpfs(DEFAULT_IPFS_API_URL, serializeAgentCard(createAgentCard(publicProfile)), fetch, { pinataJwt: step.pinataJwt })
|
|
141
|
+
const agentCardJson = serializeAgentCard(createAgentCard(publicProfile))
|
|
142
|
+
const agentCardPin = await addToIpfs(DEFAULT_IPFS_API_URL, agentCardJson, fetch, { pinataJwt: step.pinataJwt })
|
|
146
143
|
assertVerifiedPin(agentCardPin)
|
|
147
144
|
const envelope = createContinuitySnapshotEnvelope({
|
|
148
145
|
ownerAddress: wallet.account,
|
|
@@ -168,9 +165,8 @@ export async function runCreateSigning(
|
|
|
168
165
|
rpcUrl: step.registry.rpcUrl,
|
|
169
166
|
identityRegistryAddress: step.registry.identityRegistryAddress,
|
|
170
167
|
}
|
|
171
|
-
const
|
|
172
|
-
cid:
|
|
173
|
-
agentCardCid: agentCardPin.cid,
|
|
168
|
+
const agentCard: AgentCardMetadata = {
|
|
169
|
+
cid: agentCardPin.cid,
|
|
174
170
|
updatedAt: envelope.createdAt,
|
|
175
171
|
status: 'pinned',
|
|
176
172
|
}
|
|
@@ -181,7 +177,7 @@ export async function runCreateSigning(
|
|
|
181
177
|
...(typeof state.imageUrl === 'string' ? { image: state.imageUrl } : {}),
|
|
182
178
|
}, {
|
|
183
179
|
backup: { cid, envelopeVersion: envelope.envelopeVersion, createdAt: envelope.createdAt },
|
|
184
|
-
publicDiscovery: {
|
|
180
|
+
publicDiscovery: { agentCardCid: agentCard.cid, updatedAt: agentCard.updatedAt },
|
|
185
181
|
registration: { chainId: step.registry.chainId, identityRegistryAddress: step.registry.identityRegistryAddress },
|
|
186
182
|
ownerAddress: wallet.account,
|
|
187
183
|
})
|
|
@@ -197,10 +193,10 @@ export async function runCreateSigning(
|
|
|
197
193
|
agentUri,
|
|
198
194
|
metadataCid,
|
|
199
195
|
backup: { ...backup, metadataCid, agentUri },
|
|
200
|
-
|
|
196
|
+
agentCard,
|
|
201
197
|
state,
|
|
202
198
|
continuityFiles,
|
|
203
|
-
|
|
199
|
+
agentCardJson,
|
|
204
200
|
},
|
|
205
201
|
}
|
|
206
202
|
},
|
|
@@ -233,11 +229,11 @@ export async function runCreateSigning(
|
|
|
233
229
|
metadataCid: result.prepared.metadataCid,
|
|
234
230
|
state: result.prepared.state,
|
|
235
231
|
backup,
|
|
236
|
-
|
|
232
|
+
agentCard: result.prepared.agentCard,
|
|
237
233
|
}
|
|
238
234
|
await writeIdentityMarkdownScaffold(nextIdentity, {
|
|
239
235
|
...defaultContinuityFiles(nextIdentity),
|
|
240
|
-
'
|
|
236
|
+
'agent-card.json': result.prepared.agentCardJson,
|
|
241
237
|
})
|
|
242
238
|
await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'initial published snapshot' }).catch(() => null)
|
|
243
239
|
await callbacks.onIdentityComplete(nextIdentity, `ERC-8004 agent registered · #${registered.agentId.toString()}`, 'create')
|
|
@@ -46,7 +46,6 @@ type AdvancedScreenProps = {
|
|
|
46
46
|
runAdvancedSubdomainCheck: (rootName: string, label: string) => void
|
|
47
47
|
onEnsSetup: EnsEditProps['onEnsSetup']
|
|
48
48
|
onEnsLink: EnsEditProps['onEnsLink']
|
|
49
|
-
onWithdrawToken: () => void
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
export function renderAdvancedEnsPhase({
|
|
@@ -65,82 +64,7 @@ export function renderAdvancedEnsPhase({
|
|
|
65
64
|
runAdvancedSubdomainCheck,
|
|
66
65
|
onEnsSetup,
|
|
67
66
|
onEnsLink,
|
|
68
|
-
onWithdrawToken,
|
|
69
67
|
}: AdvancedScreenProps): React.ReactNode | null {
|
|
70
|
-
if (phase.kind === 'advanced-transfer-check') {
|
|
71
|
-
type TransferCheckAction = 'continue' | 'withdraw' | 'back'
|
|
72
|
-
const custody = reconciliation.custody
|
|
73
|
-
const tokenInVault = custody === 'advanced' || custody === 'mid-flow-uri-pending'
|
|
74
|
-
const tokenInOwnerWallet = custody === 'simple' || custody === 'withdrawn'
|
|
75
|
-
const probePending = custody === 'unknown' && reconciliation.rpc !== 'failing'
|
|
76
|
-
const probeFailed = reconciliation.rpc === 'failing'
|
|
77
|
-
|
|
78
|
-
const options: Array<{ value: TransferCheckAction; role?: 'section' | 'utility'; label: string; hint?: string }> = []
|
|
79
|
-
options.push({ value: 'continue', role: 'section', label: 'Setup' })
|
|
80
|
-
if (tokenInOwnerWallet) {
|
|
81
|
-
options.push({
|
|
82
|
-
value: 'continue',
|
|
83
|
-
label: 'Continue ENS Setup',
|
|
84
|
-
hint: 'Owner wallet holds this token onchain.',
|
|
85
|
-
})
|
|
86
|
-
} else if (tokenInVault) {
|
|
87
|
-
options.push({
|
|
88
|
-
value: 'withdraw',
|
|
89
|
-
label: 'Withdraw Token',
|
|
90
|
-
hint: 'Pull token out to sign ENS records. Redeposit to the Vault any time after.',
|
|
91
|
-
})
|
|
92
|
-
} else if (probePending) {
|
|
93
|
-
options.push({
|
|
94
|
-
value: 'continue',
|
|
95
|
-
label: 'Checking onchain state…',
|
|
96
|
-
hint: 'Try again in a moment.',
|
|
97
|
-
})
|
|
98
|
-
} else if (probeFailed) {
|
|
99
|
-
options.push({
|
|
100
|
-
value: 'continue',
|
|
101
|
-
label: 'Onchain check unavailable',
|
|
102
|
-
hint: 'RPC unreachable. Resolve connectivity, then retry.',
|
|
103
|
-
})
|
|
104
|
-
} else {
|
|
105
|
-
options.push({
|
|
106
|
-
value: 'continue',
|
|
107
|
-
label: 'Token Owner Unknown',
|
|
108
|
-
hint: 'Try again in a moment.',
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
options.push({ value: 'back', role: 'section', label: 'Navigation' })
|
|
112
|
-
options.push({ value: 'back', label: 'Back', hint: 'Return to setup type', role: 'utility' })
|
|
113
|
-
|
|
114
|
-
return (
|
|
115
|
-
<Surface
|
|
116
|
-
title="Token Custody Check"
|
|
117
|
-
subtitle="ENS setup continues only after the owner wallet holds this token onchain."
|
|
118
|
-
footer={footerHint('enter select · esc back')}
|
|
119
|
-
>
|
|
120
|
-
<Box marginTop={1}>
|
|
121
|
-
<Select<TransferCheckAction>
|
|
122
|
-
options={options}
|
|
123
|
-
hintLayout="inline"
|
|
124
|
-
onSubmit={choice => {
|
|
125
|
-
if (choice === 'withdraw') {
|
|
126
|
-
onWithdrawToken()
|
|
127
|
-
return
|
|
128
|
-
}
|
|
129
|
-
if (choice === 'continue' && tokenInOwnerWallet) {
|
|
130
|
-
runDiscovery('advanced')
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
if (choice === 'back') {
|
|
134
|
-
return setPhase({ kind: 'mode-select' })
|
|
135
|
-
}
|
|
136
|
-
}}
|
|
137
|
-
onCancel={() => setPhase({ kind: 'mode-select' })}
|
|
138
|
-
/>
|
|
139
|
-
</Box>
|
|
140
|
-
</Surface>
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
68
|
if (phase.kind === 'advanced-root-check') {
|
|
145
69
|
return (
|
|
146
70
|
<Surface
|
|
@@ -2,8 +2,9 @@ import React from 'react'
|
|
|
2
2
|
import { getAddress, type Address } from 'viem'
|
|
3
3
|
import type { BrowserWalletReady } from '../../wallet/browserWallet.js'
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
AGENT_TOKEN_RECORD_KEY,
|
|
6
6
|
buildAgentEnsRecords,
|
|
7
|
+
buildEnsip25Key,
|
|
7
8
|
diffRecords,
|
|
8
9
|
recordsFromTextMap,
|
|
9
10
|
} from '../../ens/agentRecords.js'
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
preflightEnsRoot,
|
|
20
21
|
preflightEnsSetup,
|
|
21
22
|
} from '../../ens/ensAutomation.js'
|
|
23
|
+
import { SUPPORTED_ERC8004_CHAINS } from '../../registry/erc8004.js'
|
|
22
24
|
import {
|
|
23
25
|
readCustodyMode,
|
|
24
26
|
readIdentityStateString,
|
|
@@ -50,7 +52,6 @@ export const EnsEditFlow: React.FC<EnsEditProps> = ({
|
|
|
50
52
|
onEnsRecordsUpdate,
|
|
51
53
|
onEnsSetup,
|
|
52
54
|
onManageOperatorWalletAccess,
|
|
53
|
-
onWithdrawToken,
|
|
54
55
|
initialView,
|
|
55
56
|
onBack,
|
|
56
57
|
}) => {
|
|
@@ -68,7 +69,7 @@ export const EnsEditFlow: React.FC<EnsEditProps> = ({
|
|
|
68
69
|
|
|
69
70
|
const [discovery, setDiscovery] = React.useState<DiscoveryState>({ status: 'idle' })
|
|
70
71
|
const [phase, setPhase] = React.useState<EnsPhase>(() => {
|
|
71
|
-
if (initialView === 'advanced' && !hasAdvancedSetup) return { kind: '
|
|
72
|
+
if (initialView === 'advanced' && !hasAdvancedSetup) return { kind: 'pick-parent', mode: 'advanced' }
|
|
72
73
|
return { kind: 'mode-select' }
|
|
73
74
|
})
|
|
74
75
|
const [validationError, setValidationError] = React.useState<string | null>(null)
|
|
@@ -137,8 +138,18 @@ export const EnsEditFlow: React.FC<EnsEditProps> = ({
|
|
|
137
138
|
setPhase({ kind: 'validating', fullName, mode, ownerAddress: phaseOwnerAddress, operatorWallet })
|
|
138
139
|
try {
|
|
139
140
|
const validation = await validateAgentEnsLink(fullName, ownerAddress)
|
|
140
|
-
const
|
|
141
|
-
?
|
|
141
|
+
const readKeys = identity.agentId
|
|
142
|
+
? [
|
|
143
|
+
...SUPPORTED_ERC8004_CHAINS.map(chain => buildEnsip25Key({
|
|
144
|
+
chainId: chain.chainId,
|
|
145
|
+
identityRegistryAddress: registry.identityRegistryAddress,
|
|
146
|
+
agentId: identity.agentId!,
|
|
147
|
+
})),
|
|
148
|
+
AGENT_TOKEN_RECORD_KEY,
|
|
149
|
+
]
|
|
150
|
+
: []
|
|
151
|
+
const currentText = validation.ok && readKeys.length > 0
|
|
152
|
+
? await readEthagentTextRecords(fullName, readKeys)
|
|
142
153
|
: {}
|
|
143
154
|
const current = recordsFromTextMap(currentText)
|
|
144
155
|
const next = buildAgentEnsRecords({
|
|
@@ -252,7 +263,17 @@ export const EnsEditFlow: React.FC<EnsEditProps> = ({
|
|
|
252
263
|
const runUnlinkEnsLoading = React.useCallback((fullName: string): void => {
|
|
253
264
|
setValidationError(null)
|
|
254
265
|
setPhase({ kind: 'unlink-loading', fullName })
|
|
255
|
-
|
|
266
|
+
const readKeys = identity.agentId
|
|
267
|
+
? [
|
|
268
|
+
...SUPPORTED_ERC8004_CHAINS.map(chain => buildEnsip25Key({
|
|
269
|
+
chainId: chain.chainId,
|
|
270
|
+
identityRegistryAddress: registry.identityRegistryAddress,
|
|
271
|
+
agentId: identity.agentId!,
|
|
272
|
+
})),
|
|
273
|
+
AGENT_TOKEN_RECORD_KEY,
|
|
274
|
+
]
|
|
275
|
+
: []
|
|
276
|
+
readEthagentTextRecords(fullName, readKeys)
|
|
256
277
|
.then(currentText => {
|
|
257
278
|
const currentRecords = recordsFromTextMap(currentText)
|
|
258
279
|
setPhase({
|
|
@@ -266,7 +287,7 @@ export const EnsEditFlow: React.FC<EnsEditProps> = ({
|
|
|
266
287
|
setValidationError(err instanceof Error ? err.message : String(err))
|
|
267
288
|
setPhase({ kind: 'mode-select' })
|
|
268
289
|
})
|
|
269
|
-
}, [])
|
|
290
|
+
}, [identity.agentId, registry.chainId, registry.identityRegistryAddress])
|
|
270
291
|
|
|
271
292
|
const maintenanceScreen = renderEnsMaintenancePhase({
|
|
272
293
|
phase,
|
|
@@ -307,7 +328,6 @@ export const EnsEditFlow: React.FC<EnsEditProps> = ({
|
|
|
307
328
|
runAdvancedSubdomainCheck,
|
|
308
329
|
onEnsSetup,
|
|
309
330
|
onEnsLink,
|
|
310
|
-
onWithdrawToken,
|
|
311
331
|
})
|
|
312
332
|
if (advancedScreen) return advancedScreen
|
|
313
333
|
|
|
@@ -82,11 +82,11 @@ export function renderEnsMaintenancePhase({
|
|
|
82
82
|
const linkHint = multiNeedsCustodySetup
|
|
83
83
|
? 'Set Advanced custody first via Custody Mode'
|
|
84
84
|
: isAdvanced
|
|
85
|
-
? '
|
|
86
|
-
: '
|
|
85
|
+
? 'Root → Name → Review → Apply'
|
|
86
|
+
: 'Root → Name → Review → Apply'
|
|
87
87
|
const options: Array<{ value: EnsAction; role?: 'section' | 'utility'; label: string; hint?: string; disabled?: boolean }> = []
|
|
88
88
|
if (currentEnsName) {
|
|
89
|
-
options.push({ value: 'unlink', label: 'Unlink Name', hint: 'Removes
|
|
89
|
+
options.push({ value: 'unlink', label: 'Unlink Name', hint: 'Removes the name from the token. Link a different one anytime.' })
|
|
90
90
|
} else {
|
|
91
91
|
options.push({
|
|
92
92
|
value: 'link',
|
|
@@ -116,10 +116,6 @@ export function renderEnsMaintenancePhase({
|
|
|
116
116
|
}
|
|
117
117
|
if (choice === 'link') {
|
|
118
118
|
if (multiNeedsCustodySetup) return
|
|
119
|
-
if (isAdvanced && savedOwnerAddress) {
|
|
120
|
-
setPhase({ kind: 'advanced-transfer-check' })
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
119
|
runDiscovery()
|
|
124
120
|
return
|
|
125
121
|
}
|
|
@@ -4,11 +4,9 @@ import { getAddress, type Address } from 'viem'
|
|
|
4
4
|
import { Surface } from '../../../ui/Surface.js'
|
|
5
5
|
import { Select, type SelectOption } from '../../../ui/Select.js'
|
|
6
6
|
import { theme } from '../../../ui/theme.js'
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type AgentEnsRecords,
|
|
11
|
-
type AgentRecordDiff,
|
|
7
|
+
import type {
|
|
8
|
+
AgentEnsRecords,
|
|
9
|
+
AgentRecordDiff,
|
|
12
10
|
} from '../../ens/agentRecords.js'
|
|
13
11
|
import type { EnsValidation } from '../../ens/ensLookup.js'
|
|
14
12
|
import type {
|
|
@@ -23,9 +21,9 @@ import {
|
|
|
23
21
|
import { ensValidationReasonText } from './state.js'
|
|
24
22
|
import { shortAddress } from '../shared/model/format.js'
|
|
25
23
|
import {
|
|
24
|
+
abbreviateHexBlobs,
|
|
26
25
|
manualReasonTitle,
|
|
27
26
|
modeSwitchHeading,
|
|
28
|
-
setupSwitchNotice,
|
|
29
27
|
} from './editCopy.js'
|
|
30
28
|
import {
|
|
31
29
|
EnsSetupRow,
|
|
@@ -114,7 +112,6 @@ export const EnsSetupReviewScreen: React.FC<EnsSetupReviewScreenProps> = ({
|
|
|
114
112
|
type Action = 'begin' | 'back'
|
|
115
113
|
const isSimple = setup.mode === 'simple'
|
|
116
114
|
const signerLabel = isSimple ? 'Connected wallet' : 'Owner wallet'
|
|
117
|
-
const switchNotice = setupSwitchNotice(currentEnsName, currentMode, setup.fullName, setup.mode)
|
|
118
115
|
const createLabel = setup.registryAction === 'create-subdomain'
|
|
119
116
|
? 'Create Subdomain'
|
|
120
117
|
: setup.registryAction === 'create-wrapped-subdomain'
|
|
@@ -140,7 +137,6 @@ export const EnsSetupReviewScreen: React.FC<EnsSetupReviewScreenProps> = ({
|
|
|
140
137
|
) : null}
|
|
141
138
|
<Box flexDirection="column">
|
|
142
139
|
<Text color={theme.dim}>{modeSwitchHeading(currentEnsName, currentMode, setup.fullName, setup.mode)}</Text>
|
|
143
|
-
{switchNotice ? <Text color={theme.dim}>{switchNotice}</Text> : null}
|
|
144
140
|
<EnsSetupRow label="ENS name" value={setup.fullName} />
|
|
145
141
|
<EnsSetupRow label="Parent root" value={setup.rootName} />
|
|
146
142
|
<EnsSetupRow label="Subdomain label" value={setup.label} />
|
|
@@ -194,7 +190,9 @@ export const EnsSetupBlockedScreen: React.FC<EnsSetupBlockedScreenProps> = ({
|
|
|
194
190
|
{isSimple
|
|
195
191
|
? <EnsSetupRow label="Wallet" value={fallback.ownerAddress ? shortAddress(fallback.ownerAddress) : shortAddress(fallback.operatorAddress)} />
|
|
196
192
|
: (fallback.ownerAddress ? <EnsSetupRow label="Owner wallet" value={shortAddress(fallback.ownerAddress)} /> : null)}
|
|
197
|
-
{fallback.nextRecords
|
|
193
|
+
{fallback.nextRecords && Object.keys(fallback.nextRecords).length > 0
|
|
194
|
+
? <EnsSetupRow label="Attestation" value="ENSIP-25 agent-registration record" />
|
|
195
|
+
: null}
|
|
198
196
|
<EnsSetupRow label="Address" value={`Set the subdomain address record to the ${isSimple ? 'connected wallet' : 'owner wallet'}.`} />
|
|
199
197
|
{!isSimple
|
|
200
198
|
? (
|
|
@@ -270,8 +268,8 @@ export const UnlinkEnsReviewScreen: React.FC<UnlinkEnsReviewScreenProps> = ({
|
|
|
270
268
|
<Text color={theme.textSubtle}>Will be cleared:</Text>
|
|
271
269
|
{changedDiffs.map(diff => (
|
|
272
270
|
<Text key={diff.key}>
|
|
273
|
-
<Text color={theme.dim}>{` ${diff.key} `}</Text>
|
|
274
|
-
<Text color={theme.accentPeriwinkle}>{
|
|
271
|
+
<Text color={theme.dim}>{` ${abbreviateHexBlobs(diff.key)} `}</Text>
|
|
272
|
+
<Text color={theme.accentPeriwinkle}>{abbreviateHexBlobs(diff.current)}</Text>
|
|
275
273
|
</Text>
|
|
276
274
|
))}
|
|
277
275
|
</Box>
|
|
@@ -342,7 +340,6 @@ export const ReviewScreen: React.FC<ReviewScreenProps> = ({
|
|
|
342
340
|
const changedDiffs = recordsDiff.filter(d => d.changed)
|
|
343
341
|
const hasRecordChanges = changedDiffs.length > 0
|
|
344
342
|
const reviewSubtitle = 'Review Ethereum Mainnet ENS address and text records before saving this ENS link. No token approval is requested.'
|
|
345
|
-
const switchNotice = setupSwitchNotice(currentEnsName, currentMode, fullName, mode)
|
|
346
343
|
|
|
347
344
|
if (!validation.ok) {
|
|
348
345
|
const reason = ensValidationReasonText(validation.reason)
|
|
@@ -396,7 +393,7 @@ export const ReviewScreen: React.FC<ReviewScreenProps> = ({
|
|
|
396
393
|
|
|
397
394
|
const options: Array<SelectOption<ReviewAction>> = [
|
|
398
395
|
{ value: 'continue', role: 'section', label: modeSwitchHeading(currentEnsName, currentMode, fullName, mode) },
|
|
399
|
-
{ value: 'continue', label: 'Continue Setup', hint: hasRecordChanges ? `Ethereum Mainnet: sign ${changedDiffs.length}
|
|
396
|
+
{ value: 'continue', label: 'Continue Setup', hint: hasRecordChanges ? `Ethereum Mainnet: sign ${changedDiffs.length} record${changedDiffs.length === 1 ? '' : 's'}; ${registryNetworkLabel}: save token URI` : `Records match; ${registryNetworkLabel}: save token URI` },
|
|
400
397
|
{ value: 'change', label: 'Pick A Different Name', hint: 'Return to the name picker' },
|
|
401
398
|
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
402
399
|
{ value: 'back', label: 'Back', hint: 'Return to Identity Hub', role: 'utility' },
|
|
@@ -414,22 +411,21 @@ export const ReviewScreen: React.FC<ReviewScreenProps> = ({
|
|
|
414
411
|
<Box marginBottom={1} flexDirection="column">
|
|
415
412
|
<Text color={theme.dim}>Current: <Text color={currentEnsName ? theme.text : theme.dim}>{currentEnsName || 'None'}</Text></Text>
|
|
416
413
|
<Text color={theme.dim}>Next: <Text color={theme.text}>{fullName}</Text></Text>
|
|
417
|
-
{switchNotice ? <Text color={theme.dim}>{switchNotice}</Text> : null}
|
|
418
414
|
</Box>
|
|
419
415
|
)
|
|
420
416
|
: null}
|
|
421
417
|
{recordsDiff.map(diff => (
|
|
422
418
|
<Text key={diff.key}>
|
|
423
|
-
<Text color={theme.dim}>{`- ${
|
|
419
|
+
<Text color={theme.dim}>{`- ${abbreviateHexBlobs(diff.key)}: `}</Text>
|
|
424
420
|
{diff.changed
|
|
425
421
|
? (
|
|
426
422
|
<>
|
|
427
|
-
{renderRecordValue(diff.
|
|
423
|
+
{renderRecordValue(diff.current)}
|
|
428
424
|
<Text color={theme.dim}>{' → '}</Text>
|
|
429
|
-
{renderRecordValue(diff.
|
|
425
|
+
{renderRecordValue(diff.next)}
|
|
430
426
|
</>
|
|
431
427
|
)
|
|
432
|
-
: renderRecordValue(diff.
|
|
428
|
+
: renderRecordValue(diff.next)}
|
|
433
429
|
</Text>
|
|
434
430
|
))}
|
|
435
431
|
{!hasRecordChanges
|
|
@@ -4,10 +4,6 @@ import type { Address } from 'viem'
|
|
|
4
4
|
import { Surface } from '../../../ui/Surface.js'
|
|
5
5
|
import { TextInput } from '../../../ui/TextInput.js'
|
|
6
6
|
import { theme } from '../../../ui/theme.js'
|
|
7
|
-
import {
|
|
8
|
-
formatRecordValue,
|
|
9
|
-
type AgentEnsRecordState,
|
|
10
|
-
} from '../../ens/agentRecords.js'
|
|
11
7
|
import {
|
|
12
8
|
isEthDomain,
|
|
13
9
|
sanitizeSubdomainPrefix,
|
|
@@ -24,9 +20,11 @@ import type { EnsEditProps } from './types.js'
|
|
|
24
20
|
|
|
25
21
|
export const footerHint = (hint: string) => <Text color={theme.dim}>{hint}</Text>
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
import { abbreviateHexBlobs } from './editCopy.js'
|
|
24
|
+
|
|
25
|
+
export const renderRecordValue = (value: string) =>
|
|
28
26
|
value
|
|
29
|
-
? <Text color={theme.accentPeriwinkle}>{
|
|
27
|
+
? <Text color={theme.accentPeriwinkle}>{abbreviateHexBlobs(value)}</Text>
|
|
30
28
|
: <Text color={theme.dim}>Unset</Text>
|
|
31
29
|
|
|
32
30
|
export function rootErrorMessage(
|
|
@@ -135,7 +135,7 @@ export function renderSimpleEnsPhase({
|
|
|
135
135
|
]
|
|
136
136
|
: []),
|
|
137
137
|
{ value: 'open-ens-domains' as DomainAction, role: 'section' as const, label: 'No Parent Name?' },
|
|
138
|
-
{ value: 'open-ens-domains' as DomainAction, label: 'Register .eth Name', hint: '
|
|
138
|
+
{ value: 'open-ens-domains' as DomainAction, label: 'Register .eth Name', hint: 'Opens ENS app; return once this wallet owns one' },
|
|
139
139
|
...(noOwnedNames || discovery.status === 'ok'
|
|
140
140
|
? [{ value: 'retry' as DomainAction, label: 'Scan Again', hint: 'Re-run root .eth name discovery for this wallet' }]
|
|
141
141
|
: []),
|
|
@@ -356,7 +356,7 @@ export function renderSimpleEnsPhase({
|
|
|
356
356
|
onEnsLink(phase.fullName, linkOptions)
|
|
357
357
|
}}
|
|
358
358
|
onCheckAgain={() => { void runValidation(phase.fullName, phase.mode, phase.ownerAddress, phase.operatorWallet) }}
|
|
359
|
-
onChange={() => setPhase(
|
|
359
|
+
onChange={() => setPhase({ kind: 'pick-parent', mode: phase.mode })}
|
|
360
360
|
onCreate={phase.mode === 'simple' && !phase.validation.ok && phase.validation.reason === 'no-owner'
|
|
361
361
|
? () => runSimpleCreatePreflight(phase.fullName)
|
|
362
362
|
: undefined}
|
|
@@ -34,7 +34,6 @@ type EnsFlowProps = {
|
|
|
34
34
|
onWalletReady: (session: BrowserWalletReady | null) => void
|
|
35
35
|
onTriggerRebackup: (backStep: Step, profileUpdates?: ProfileUpdates) => void
|
|
36
36
|
onTriggerPublicProfileSave: (backStep: Step, profileUpdates: ProfileUpdates) => void
|
|
37
|
-
onWithdrawTokenForEns: (step: Step) => void
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
export function isEnsStep(step: Step): step is IdentityHubEnsStep {
|
|
@@ -59,7 +58,6 @@ export const EnsFlow: React.FC<EnsFlowProps> = ({
|
|
|
59
58
|
onWalletReady,
|
|
60
59
|
onTriggerRebackup,
|
|
61
60
|
onTriggerPublicProfileSave,
|
|
62
|
-
onWithdrawTokenForEns,
|
|
63
61
|
}) => {
|
|
64
62
|
if (step.kind === 'manage-ens-operators') {
|
|
65
63
|
return (
|
|
@@ -81,7 +79,6 @@ export const EnsFlow: React.FC<EnsFlowProps> = ({
|
|
|
81
79
|
<EditProfileFlow
|
|
82
80
|
step={step}
|
|
83
81
|
reconciliation={reconciliation}
|
|
84
|
-
onWithdrawToken={() => onWithdrawTokenForEns(step)}
|
|
85
82
|
onNameSubmit={name => {
|
|
86
83
|
if (step.kind !== 'edit-profile-name') return
|
|
87
84
|
onSetStep({
|