ethagent 1.1.2 → 2.0.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/LICENSE +21 -21
- package/README.md +124 -32
- package/package.json +8 -3
- package/src/app/FirstRun.tsx +190 -146
- package/src/app/FirstRunTimeline.tsx +47 -0
- package/src/app/input/AppInputProvider.tsx +1 -1
- package/src/app/keybindings/KeybindingProvider.tsx +1 -1
- package/src/chat/ChatBottomPane.tsx +0 -1
- package/src/chat/ChatInput.tsx +6 -6
- package/src/chat/ChatScreen.tsx +35 -15
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +10 -22
- package/src/chat/CopyPicker.tsx +0 -1
- package/src/chat/MessageList.tsx +62 -45
- package/src/chat/PermissionPrompt.tsx +13 -9
- package/src/chat/PlanApprovalView.tsx +3 -3
- package/src/chat/ResumeView.tsx +1 -4
- package/src/chat/RewindView.tsx +2 -2
- package/src/chat/chatInputState.ts +1 -1
- package/src/chat/chatScreenUtils.ts +22 -11
- package/src/chat/chatSessionState.ts +2 -2
- package/src/chat/chatTurnOrchestrator.ts +16 -81
- package/src/chat/commands.ts +1 -1
- package/src/chat/textCursor.ts +1 -1
- package/src/chat/transcriptViewport.ts +2 -7
- package/src/cli/ResetConfirmView.tsx +1 -1
- package/src/cli/main.tsx +9 -3
- package/src/cli/preview.tsx +0 -5
- package/src/cli/updateNotice.ts +4 -2
- package/src/identity/continuity/editor.ts +7 -107
- package/src/identity/continuity/envelope.ts +1048 -40
- package/src/identity/continuity/history.ts +4 -4
- package/src/identity/continuity/localBackup.ts +249 -0
- package/src/identity/continuity/privateEdit/apply.ts +170 -0
- package/src/identity/continuity/privateEdit/diff.ts +82 -0
- package/src/identity/continuity/privateEdit/files.ts +23 -0
- package/src/identity/continuity/privateEdit/types.ts +28 -0
- package/src/identity/continuity/privateEdit.ts +10 -298
- package/src/identity/continuity/publicSkills.ts +8 -9
- package/src/identity/continuity/snapshots.ts +17 -6
- package/src/identity/continuity/storage/defaults.ts +111 -0
- package/src/identity/continuity/storage/files.ts +72 -0
- package/src/identity/continuity/storage/markdown.ts +81 -0
- package/src/identity/continuity/storage/paths.ts +24 -0
- package/src/identity/continuity/storage/scaffold.ts +124 -0
- package/src/identity/continuity/storage/status.ts +86 -0
- package/src/identity/continuity/storage/types.ts +27 -0
- package/src/identity/continuity/storage.ts +32 -507
- package/src/identity/continuity/zipWriter.ts +95 -0
- package/src/identity/crypto/backupEnvelope.ts +14 -247
- package/src/identity/crypto/eth.ts +7 -7
- package/src/identity/ens/agentRecords.ts +96 -0
- package/src/identity/ens/ensAutomation/contracts.ts +38 -0
- package/src/identity/ens/ensAutomation/delete.ts +80 -0
- package/src/identity/ens/ensAutomation/names.ts +14 -0
- package/src/identity/ens/ensAutomation/operators.ts +29 -0
- package/src/identity/ens/ensAutomation/read.ts +114 -0
- package/src/identity/ens/ensAutomation/root.ts +63 -0
- package/src/identity/ens/ensAutomation/setup.ts +284 -0
- package/src/identity/ens/ensAutomation/transactions.ts +107 -0
- package/src/identity/ens/ensAutomation/types.ts +126 -0
- package/src/identity/ens/ensAutomation.ts +29 -0
- package/src/identity/ens/ensLookup/client.ts +43 -0
- package/src/identity/ens/ensLookup/constants.ts +26 -0
- package/src/identity/ens/ensLookup/discovery.ts +70 -0
- package/src/identity/ens/ensLookup/names.ts +34 -0
- package/src/identity/ens/ensLookup/records.ts +45 -0
- package/src/identity/ens/ensLookup/resolve.ts +75 -0
- package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
- package/src/identity/ens/ensLookup/types.ts +38 -0
- package/src/identity/ens/ensLookup/validation.ts +72 -0
- package/src/identity/ens/ensLookup.ts +19 -0
- package/src/identity/ens/ensRegistration.ts +199 -0
- package/src/identity/ens/resolverDelegation.ts +48 -0
- package/src/identity/hub/IdentityHub.tsx +13 -817
- package/src/identity/hub/OperationalRoutes.tsx +370 -0
- package/src/identity/hub/Routes.tsx +361 -0
- package/src/identity/hub/advancedEnsValidation.ts +45 -0
- package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
- package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
- package/src/identity/hub/components/FlowTimeline.tsx +27 -0
- package/src/identity/hub/components/IdentitySummary.tsx +190 -0
- package/src/identity/hub/components/MenuScreen.tsx +237 -0
- package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
- package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
- package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
- package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
- package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
- package/src/identity/hub/effects/create.ts +310 -0
- package/src/identity/hub/effects/ens/flows.ts +218 -0
- package/src/identity/hub/effects/ens/index.ts +11 -0
- package/src/identity/hub/effects/ens/transactions.ts +239 -0
- package/src/identity/hub/effects/index.ts +74 -0
- package/src/identity/hub/effects/profile/profileState.ts +173 -0
- package/src/identity/hub/effects/publicProfile/index.ts +5 -0
- package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
- package/src/identity/hub/effects/rebackup/index.ts +7 -0
- package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
- package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
- package/src/identity/hub/effects/receipts.ts +46 -0
- package/src/identity/hub/effects/restore/apply.ts +112 -0
- package/src/identity/hub/effects/restore/auth.ts +159 -0
- package/src/identity/hub/effects/restore/discover.ts +86 -0
- package/src/identity/hub/effects/restore/envelopes.ts +21 -0
- package/src/identity/hub/effects/restore/fetch.ts +25 -0
- package/src/identity/hub/effects/restore/index.ts +22 -0
- package/src/identity/hub/effects/restore/recovery.ts +135 -0
- package/src/identity/hub/effects/restore/resolve.ts +102 -0
- package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
- package/src/identity/hub/effects/restore/shared.ts +91 -0
- package/src/identity/hub/effects/restoreAdmin.ts +93 -0
- package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
- package/src/identity/hub/effects/shared/snapshot.ts +336 -0
- package/src/identity/hub/effects/shared/sync.ts +190 -0
- package/src/identity/hub/effects/token-transfer/index.ts +6 -0
- package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
- package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
- package/src/identity/hub/effects/types.ts +53 -0
- package/src/identity/hub/effects/vault/preflight.ts +50 -0
- package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
- package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
- package/src/identity/hub/{screens → flows/continuity}/RecoveryConfirmScreen.tsx +28 -19
- package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
- package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
- package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
- package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
- package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
- package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
- package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
- package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
- package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
- package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
- package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
- package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
- package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
- package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
- package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
- package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
- package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
- package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
- package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
- package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
- package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
- package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
- package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
- package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
- package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +23 -44
- package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
- package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
- package/src/identity/hub/identityHubReducer.ts +164 -99
- package/src/identity/hub/model/continuity.ts +94 -0
- package/src/identity/hub/model/copy.ts +35 -0
- package/src/identity/hub/model/custody.ts +54 -0
- package/src/identity/hub/model/ens.ts +49 -0
- package/src/identity/hub/model/errors.ts +140 -0
- package/src/identity/hub/model/format.ts +15 -0
- package/src/identity/hub/model/identity.ts +94 -0
- package/src/identity/hub/model/network.ts +32 -0
- package/src/identity/hub/model/transfer.ts +57 -0
- package/src/identity/hub/operatorWallets.ts +131 -0
- package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
- package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
- package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
- package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
- package/src/identity/hub/reconciliation/index.ts +21 -0
- package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
- package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
- package/src/identity/hub/txGuard.ts +51 -0
- package/src/identity/hub/types.ts +17 -0
- package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
- package/src/identity/hub/useIdentityHubController.ts +396 -0
- package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
- package/src/identity/hub/utils.ts +79 -0
- package/src/identity/identityCompat.ts +34 -0
- package/src/identity/profile/agentIcon.ts +61 -0
- package/src/identity/profile/imagePicker.ts +12 -12
- package/src/identity/registry/erc8004/abi.ts +14 -0
- package/src/identity/registry/erc8004/chains.ts +150 -0
- package/src/identity/registry/erc8004/client.ts +11 -0
- package/src/identity/registry/erc8004/discovery.ts +511 -0
- package/src/identity/registry/erc8004/metadata.ts +335 -0
- package/src/identity/registry/erc8004/ownership.ts +121 -0
- package/src/identity/registry/erc8004/preflight.ts +123 -0
- package/src/identity/registry/erc8004/transactions.ts +77 -0
- package/src/identity/registry/erc8004/types.ts +88 -0
- package/src/identity/registry/erc8004/uri.ts +59 -0
- package/src/identity/registry/erc8004/utils.ts +58 -0
- package/src/identity/registry/erc8004.ts +53 -1106
- package/src/identity/registry/fieldParsers.ts +28 -0
- package/src/identity/registry/operatorVault/bytecode.ts +98 -0
- package/src/identity/registry/operatorVault/constants.ts +38 -0
- package/src/identity/registry/operatorVault/read.ts +246 -0
- package/src/identity/registry/operatorVault/transactions.ts +81 -0
- package/src/identity/registry/operatorVault.ts +44 -0
- package/src/identity/storage/ipfs.ts +26 -24
- package/src/identity/wallet/browserWallet/gas.ts +41 -0
- package/src/identity/wallet/browserWallet/html.ts +106 -0
- package/src/identity/wallet/browserWallet/http.ts +28 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
- package/src/identity/wallet/browserWallet/requests.ts +191 -0
- package/src/identity/wallet/browserWallet/session.ts +325 -0
- package/src/identity/wallet/browserWallet/types.ts +192 -0
- package/src/identity/wallet/browserWallet/validation.ts +74 -0
- package/src/identity/wallet/browserWallet.ts +30 -393
- package/src/identity/wallet/page/constants.ts +5 -0
- package/src/identity/wallet/page/controller.ts +251 -0
- package/src/identity/wallet/page/copy.ts +340 -0
- package/src/identity/wallet/page/grainient.ts +278 -0
- package/src/identity/wallet/page/html.ts +28 -0
- package/src/identity/wallet/page/markup.ts +50 -0
- package/src/identity/wallet/page/state.ts +9 -0
- package/src/identity/wallet/page/styles/base.ts +259 -0
- package/src/identity/wallet/page/styles/components.ts +262 -0
- package/src/identity/wallet/page/styles/index.ts +5 -0
- package/src/identity/wallet/page/styles/responsive.ts +247 -0
- package/src/identity/wallet/page/types.ts +47 -0
- package/src/identity/wallet/page/view.ts +535 -0
- package/src/identity/wallet/page/walletProvider.ts +70 -0
- package/src/identity/wallet/page.tsx +38 -0
- package/src/identity/wallet/walletPurposeCompat.ts +27 -0
- package/src/mcp/manager.ts +0 -1
- package/src/models/ModelPicker.tsx +36 -30
- package/src/models/catalog.ts +5 -2
- package/src/models/huggingface.ts +9 -9
- package/src/models/llamacpp.ts +13 -13
- package/src/models/modelDisplay.ts +75 -0
- package/src/models/modelPickerOptions.ts +16 -3
- package/src/models/modelRecommendation.ts +0 -1
- package/src/providers/errors.ts +16 -0
- package/src/providers/gemini.ts +252 -39
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +1 -1
- package/src/runtime/sessionMode.ts +1 -1
- package/src/runtime/systemPrompt.ts +2 -0
- package/src/runtime/toolExecution.ts +18 -22
- package/src/runtime/toolIntent.ts +0 -20
- package/src/runtime/turn.ts +0 -92
- package/src/storage/atomicWrite.ts +4 -1
- package/src/storage/config.ts +181 -5
- package/src/storage/identity.ts +9 -3
- package/src/storage/secrets.ts +2 -2
- package/src/tools/bashSafety.ts +8 -0
- package/src/tools/changeDirectoryTool.ts +1 -1
- package/src/tools/deleteFileTool.ts +4 -4
- package/src/tools/editTool.ts +4 -4
- package/src/tools/editUtils.ts +5 -5
- package/src/tools/privateContinuityEditTool.ts +4 -5
- package/src/tools/privateContinuityReadTool.ts +1 -2
- package/src/tools/registry.ts +30 -0
- package/src/tools/writeFileTool.ts +5 -5
- package/src/ui/BrandSplash.tsx +20 -85
- package/src/ui/ProgressBar.tsx +3 -5
- package/src/ui/Select.tsx +20 -8
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +2 -2
- package/src/ui/TextInput.tsx +63 -20
- package/src/ui/theme.ts +7 -34
- package/src/utils/openExternal.ts +21 -0
- package/src/utils/withRetry.ts +47 -3
- package/src/identity/hub/identityHubEffects.ts +0 -937
- package/src/identity/hub/identityHubModel.ts +0 -371
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -156
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -146
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -106
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
- package/src/identity/wallet/wallet-page/wallet.html +0 -1202
- /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import type { Address } from 'viem'
|
|
4
|
+
import { Surface } from '../../../../ui/Surface.js'
|
|
5
|
+
import { TextInput } from '../../../../ui/TextInput.js'
|
|
6
|
+
import { theme } from '../../../../ui/theme.js'
|
|
7
|
+
import {
|
|
8
|
+
formatRecordValue,
|
|
9
|
+
type AgentEnsRecordState,
|
|
10
|
+
} from '../../../ens/agentRecords.js'
|
|
11
|
+
import {
|
|
12
|
+
isEthDomain,
|
|
13
|
+
sanitizeSubdomainPrefix,
|
|
14
|
+
} from '../../../ens/ensLookup.js'
|
|
15
|
+
import {
|
|
16
|
+
displayCustodyMode,
|
|
17
|
+
readIdentityStateString,
|
|
18
|
+
type CustodyMode,
|
|
19
|
+
} from '../../model/custody.js'
|
|
20
|
+
import { ensValidationReasonText } from '../../model/ens.js'
|
|
21
|
+
import { shortAddress } from '../../model/format.js'
|
|
22
|
+
import { readValidationFromState } from './ensEditCopy.js'
|
|
23
|
+
import type { EnsEditProps } from './ensEditTypes.js'
|
|
24
|
+
|
|
25
|
+
export const footerHint = (hint: string) => <Text color={theme.dim}>{hint}</Text>
|
|
26
|
+
|
|
27
|
+
export const renderRecordValue = (field: keyof AgentEnsRecordState, value: string) =>
|
|
28
|
+
value
|
|
29
|
+
? <Text color={theme.accentPeriwinkle}>{formatRecordValue(field, value)}</Text>
|
|
30
|
+
: <Text color={theme.dim}>Unset</Text>
|
|
31
|
+
|
|
32
|
+
export function rootErrorMessage(
|
|
33
|
+
reason: 'invalid-root' | 'missing-token-id' | 'root-not-owned' | 'wrapped-parent' | 'root-owner-mismatch' | 'token-owner-mismatch' | 'token-owner-lookup-failed' | 'lookup-failed',
|
|
34
|
+
detail: string,
|
|
35
|
+
rootName: string,
|
|
36
|
+
): string {
|
|
37
|
+
switch (reason) {
|
|
38
|
+
case 'invalid-root':
|
|
39
|
+
return 'Enter the parent .eth name, e.g. name.eth'
|
|
40
|
+
case 'missing-token-id':
|
|
41
|
+
return 'This identity is missing an ERC-8004 token id'
|
|
42
|
+
case 'root-not-owned':
|
|
43
|
+
return `${rootName} does not have an ENS manager on Ethereum mainnet. Switch wallets or pick a different parent name.`
|
|
44
|
+
case 'wrapped-parent':
|
|
45
|
+
return `${rootName} ENS NameWrapper ownership could not be verified: ${detail}`
|
|
46
|
+
case 'root-owner-mismatch':
|
|
47
|
+
return `Connected wallet does not manage ${rootName}. ${detail}`
|
|
48
|
+
case 'token-owner-mismatch':
|
|
49
|
+
return `Connected wallet manages ${rootName} but does not own this ERC-8004 token. ${detail}. Move the token to this wallet, then submit again.`
|
|
50
|
+
case 'token-owner-lookup-failed':
|
|
51
|
+
return `Could not verify ERC-8004 token owner: ${detail}`
|
|
52
|
+
case 'lookup-failed':
|
|
53
|
+
return `ENS lookup failed: ${detail}`
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const EnsSetupRow: React.FC<{ label: string; value: string; muted?: boolean }> = ({ label, value, muted }) => (
|
|
58
|
+
<Box flexDirection="row">
|
|
59
|
+
<Box width={16}>
|
|
60
|
+
<Text color={theme.dim}>{label}</Text>
|
|
61
|
+
</Box>
|
|
62
|
+
<Box flexShrink={1}>
|
|
63
|
+
<Text color={muted ? theme.dim : theme.text}>{value}</Text>
|
|
64
|
+
</Box>
|
|
65
|
+
</Box>
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
export const EnsStatusBanner: React.FC<{ identity: EnsEditProps['identity']; noRootEnsName?: boolean }> = ({ identity, noRootEnsName }) => {
|
|
69
|
+
const ensName = readIdentityStateString(identity.state, 'ensName')
|
|
70
|
+
if (!ensName) {
|
|
71
|
+
const status = noRootEnsName
|
|
72
|
+
? 'Not Linked. This wallet does not own a root .eth ENS name.'
|
|
73
|
+
: 'Not Linked. Choose a setup below.'
|
|
74
|
+
return <Text color={theme.dim}>Status: <Text color={theme.dim}>{status}</Text></Text>
|
|
75
|
+
}
|
|
76
|
+
const validation = readValidationFromState(identity.state)
|
|
77
|
+
if (validation?.ok) {
|
|
78
|
+
return <Text color={theme.dim}>Status: <Text color={theme.accentPeriwinkle}>linked, {ensName}</Text></Text>
|
|
79
|
+
}
|
|
80
|
+
return (
|
|
81
|
+
<Text color={theme.dim}>
|
|
82
|
+
Status: <Text color={theme.accentError}>issue, {ensName}</Text>
|
|
83
|
+
<Text color={theme.dim}> ({ensValidationReasonText(validation?.reason)})</Text>
|
|
84
|
+
</Text>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
type AssignEnsCurrentSetupProps = {
|
|
89
|
+
currentEnsName: string
|
|
90
|
+
currentMode: CustodyMode | undefined
|
|
91
|
+
ownerAddress: string
|
|
92
|
+
operatorAddress: string
|
|
93
|
+
tokenNetworkLabel: string
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const AssignEnsCurrentSetup: React.FC<AssignEnsCurrentSetupProps> = ({
|
|
97
|
+
currentEnsName,
|
|
98
|
+
currentMode,
|
|
99
|
+
ownerAddress,
|
|
100
|
+
operatorAddress,
|
|
101
|
+
tokenNetworkLabel,
|
|
102
|
+
}) => {
|
|
103
|
+
const modeLabel = displayCustodyMode(currentMode)
|
|
104
|
+
return (
|
|
105
|
+
<Box marginTop={1} flexDirection="column">
|
|
106
|
+
<Text color={theme.dim}>Current Setup</Text>
|
|
107
|
+
<EnsSetupRow label="ENS name" value={currentEnsName || 'None'} muted={!currentEnsName} />
|
|
108
|
+
<EnsSetupRow label="Custody" value={modeLabel} />
|
|
109
|
+
<EnsSetupRow label="ENS network" value="Ethereum Mainnet" />
|
|
110
|
+
<EnsSetupRow label="Token network" value={tokenNetworkLabel} />
|
|
111
|
+
{currentMode === 'advanced'
|
|
112
|
+
? (
|
|
113
|
+
<>
|
|
114
|
+
{ownerAddress ? <EnsSetupRow label="Owner wallet" value={shortAddress(ownerAddress)} /> : null}
|
|
115
|
+
{operatorAddress ? <EnsSetupRow label="Operator wallet" value={shortAddress(operatorAddress)} /> : null}
|
|
116
|
+
</>
|
|
117
|
+
)
|
|
118
|
+
: null}
|
|
119
|
+
</Box>
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type SubdomainEntryProps = {
|
|
124
|
+
parent: string
|
|
125
|
+
ownerAddress: Address
|
|
126
|
+
suggestion: string
|
|
127
|
+
error?: string
|
|
128
|
+
onConfirm: (fullName: string) => void
|
|
129
|
+
onBack: () => void
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const SubdomainEntry: React.FC<SubdomainEntryProps> = ({ parent, ownerAddress, suggestion, error, onConfirm, onBack }) => (
|
|
133
|
+
<Surface
|
|
134
|
+
title={`Subdomain of ${parent}`}
|
|
135
|
+
footer={footerHint('enter continues · esc back')}
|
|
136
|
+
>
|
|
137
|
+
<Text color={theme.dim}>Type the subdomain name that goes before .{parent}.</Text>
|
|
138
|
+
<Text color={theme.dim}>Use lowercase letters, digits, and hyphens.</Text>
|
|
139
|
+
{error ? <Text color={theme.accentError}>{error}</Text> : null}
|
|
140
|
+
<TextInput
|
|
141
|
+
key={`edit-ens-subdomain-${parent}`}
|
|
142
|
+
initialValue={suggestion}
|
|
143
|
+
placeholder="subdomain name"
|
|
144
|
+
validate={value => {
|
|
145
|
+
const v = sanitizeSubdomainPrefix(value)
|
|
146
|
+
if (!v) return 'Subdomain name cannot be empty'
|
|
147
|
+
if (value.includes('.')) return 'Enter only the subdomain name, not the full ENS name'
|
|
148
|
+
if (!isEthDomain(`${v}.${parent}`)) return 'Invalid characters in subdomain name'
|
|
149
|
+
return null
|
|
150
|
+
}}
|
|
151
|
+
onSubmit={value => {
|
|
152
|
+
const prefix = sanitizeSubdomainPrefix(value)
|
|
153
|
+
if (!prefix) return
|
|
154
|
+
onConfirm(`${prefix}.${parent}`)
|
|
155
|
+
}}
|
|
156
|
+
onCancel={onBack}
|
|
157
|
+
/>
|
|
158
|
+
<Box marginTop={1}>
|
|
159
|
+
<Text color={theme.dim}>Wallet: {shortAddress(ownerAddress)}</Text>
|
|
160
|
+
</Box>
|
|
161
|
+
</Surface>
|
|
162
|
+
)
|
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { formatEther, type Address } from 'viem'
|
|
4
|
+
import { Surface } from '../../../../ui/Surface.js'
|
|
5
|
+
import { Select, type SelectOption } from '../../../../ui/Select.js'
|
|
6
|
+
import { TextInput } from '../../../../ui/TextInput.js'
|
|
7
|
+
import { Spinner } from '../../../../ui/Spinner.js'
|
|
8
|
+
import { theme } from '../../../../ui/theme.js'
|
|
9
|
+
import {
|
|
10
|
+
createMainnetClient,
|
|
11
|
+
isEthDomain,
|
|
12
|
+
normalizeEthDomain,
|
|
13
|
+
} from '../../../ens/ensLookup.js'
|
|
14
|
+
import {
|
|
15
|
+
generateRegistrationSecret,
|
|
16
|
+
ONE_YEAR_SECONDS,
|
|
17
|
+
readNameAvailable,
|
|
18
|
+
readRentPrice,
|
|
19
|
+
validateRegistrableName,
|
|
20
|
+
} from '../../../ens/ensRegistration.js'
|
|
21
|
+
import { isRootEthName } from '../../../ens/ensAutomation.js'
|
|
22
|
+
import type { Erc8004RegistryConfig } from '../../../registry/erc8004.js'
|
|
23
|
+
import type { BrowserWalletReady } from '../../../wallet/browserWallet.js'
|
|
24
|
+
import {
|
|
25
|
+
readIdentityStateString,
|
|
26
|
+
type CustodyMode,
|
|
27
|
+
} from '../../model/custody.js'
|
|
28
|
+
import { shortAddress } from '../../model/format.js'
|
|
29
|
+
import {
|
|
30
|
+
recordsDiffHasChanges,
|
|
31
|
+
type EnsLinkOptions,
|
|
32
|
+
} from './ensEditCopy.js'
|
|
33
|
+
import {
|
|
34
|
+
EnsSetupRow,
|
|
35
|
+
EnsStatusBanner,
|
|
36
|
+
footerHint,
|
|
37
|
+
SubdomainEntry,
|
|
38
|
+
} from './EnsEditShared.js'
|
|
39
|
+
import {
|
|
40
|
+
EnsSetupBlockedScreen,
|
|
41
|
+
EnsSetupReviewScreen,
|
|
42
|
+
ReviewScreen,
|
|
43
|
+
SimpleEnsIssueScreen,
|
|
44
|
+
} from './EnsEditReviewScreens.js'
|
|
45
|
+
import {
|
|
46
|
+
EscCancel,
|
|
47
|
+
RegisterRootCommitRunner,
|
|
48
|
+
RegisterRootTxRunner,
|
|
49
|
+
RegisterRootWaitScreen,
|
|
50
|
+
} from './EnsEditRunners.js'
|
|
51
|
+
import type {
|
|
52
|
+
DiscoveryState,
|
|
53
|
+
EnsEditProps,
|
|
54
|
+
EnsPhase,
|
|
55
|
+
} from './ensEditTypes.js'
|
|
56
|
+
|
|
57
|
+
type SimpleScreenProps = {
|
|
58
|
+
phase: EnsPhase
|
|
59
|
+
discovery: DiscoveryState
|
|
60
|
+
ownerAddress: Address
|
|
61
|
+
discoveryStartedAt: number
|
|
62
|
+
validationError: string | null
|
|
63
|
+
currentEnsName: string
|
|
64
|
+
savedCustodyMode: CustodyMode | undefined
|
|
65
|
+
registryNetworkLabel: string
|
|
66
|
+
registry: Erc8004RegistryConfig
|
|
67
|
+
agentNameSuggestion: string
|
|
68
|
+
operatorWalletSession: BrowserWalletReady | null
|
|
69
|
+
setOperatorWalletSession: (session: BrowserWalletReady | null) => void
|
|
70
|
+
setPhase: (phase: EnsPhase) => void
|
|
71
|
+
cancelDiscoveryToModeSelect: () => void
|
|
72
|
+
runDiscovery: () => void
|
|
73
|
+
runValidation: (fullName: string, mode: 'simple' | 'advanced', phaseOwnerAddress?: Address, operatorWallet?: Address) => Promise<void>
|
|
74
|
+
backToSimpleSubdomain: (fullName: string) => void
|
|
75
|
+
runSimpleCreatePreflight: (fullName: string) => void
|
|
76
|
+
onEnsSetup: EnsEditProps['onEnsSetup']
|
|
77
|
+
onEnsLink: EnsEditProps['onEnsLink']
|
|
78
|
+
onEnsRecordsUpdate: EnsEditProps['onEnsRecordsUpdate']
|
|
79
|
+
identity: EnsEditProps['identity']
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function renderSimpleEnsPhase({
|
|
83
|
+
phase,
|
|
84
|
+
discovery,
|
|
85
|
+
ownerAddress,
|
|
86
|
+
discoveryStartedAt,
|
|
87
|
+
validationError,
|
|
88
|
+
currentEnsName,
|
|
89
|
+
savedCustodyMode,
|
|
90
|
+
registryNetworkLabel,
|
|
91
|
+
registry,
|
|
92
|
+
agentNameSuggestion,
|
|
93
|
+
operatorWalletSession,
|
|
94
|
+
setOperatorWalletSession,
|
|
95
|
+
setPhase,
|
|
96
|
+
cancelDiscoveryToModeSelect,
|
|
97
|
+
runDiscovery,
|
|
98
|
+
runValidation,
|
|
99
|
+
backToSimpleSubdomain,
|
|
100
|
+
runSimpleCreatePreflight,
|
|
101
|
+
onEnsSetup,
|
|
102
|
+
onEnsLink,
|
|
103
|
+
onEnsRecordsUpdate,
|
|
104
|
+
identity,
|
|
105
|
+
}: SimpleScreenProps): React.ReactNode | null {
|
|
106
|
+
const statusBanner = (
|
|
107
|
+
<EnsStatusBanner identity={identity} />
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if (phase.kind === 'discovering' || discovery.status === 'loading') {
|
|
111
|
+
return (
|
|
112
|
+
<Surface
|
|
113
|
+
title="Assign ENS Name"
|
|
114
|
+
subtitle="Reading your primary ENS name from Ethereum mainnet."
|
|
115
|
+
footer={footerHint('esc cancels')}
|
|
116
|
+
>
|
|
117
|
+
<Box marginTop={1}>
|
|
118
|
+
<Spinner
|
|
119
|
+
label={`Looking up root ENS names for ${shortAddress(ownerAddress)}...`}
|
|
120
|
+
startedAt={discoveryStartedAt}
|
|
121
|
+
/>
|
|
122
|
+
</Box>
|
|
123
|
+
<EscCancel onCancel={cancelDiscoveryToModeSelect} />
|
|
124
|
+
</Surface>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (phase.kind === 'pick-parent') {
|
|
129
|
+
type DomainAction = `pick:${string}` | 'register-root' | 'manual' | 'retry' | 'back'
|
|
130
|
+
const ownedNames = discovery.status === 'ok' || discovery.status === 'error' ? discovery.names : []
|
|
131
|
+
const errorMessage = discovery.status === 'error' ? 'Root ENS Lookup Failed' : null
|
|
132
|
+
const warningMessage = discovery.status === 'ok' ? discovery.warning : null
|
|
133
|
+
|
|
134
|
+
const noOwnedNames = discovery.status === 'ok' && ownedNames.length === 0
|
|
135
|
+
|
|
136
|
+
const options: Array<SelectOption<DomainAction>> = [
|
|
137
|
+
...(ownedNames.length > 0
|
|
138
|
+
? [
|
|
139
|
+
{ value: 'pick:section' as DomainAction, role: 'section' as const, label: 'Your ENS Names' },
|
|
140
|
+
...ownedNames.map(name => ({
|
|
141
|
+
value: `pick:${name}` as DomainAction,
|
|
142
|
+
label: name,
|
|
143
|
+
hint: `Next, choose the subdomain under ${name}`,
|
|
144
|
+
})),
|
|
145
|
+
]
|
|
146
|
+
: []),
|
|
147
|
+
{ value: 'register-root' as DomainAction, role: 'section' as const, label: 'Get a New One' },
|
|
148
|
+
{ value: 'register-root' as DomainAction, label: 'Register New ENS Name', hint: 'Register an ENS name through ENS to use as a root' },
|
|
149
|
+
...(noOwnedNames
|
|
150
|
+
? [{ value: 'retry' as DomainAction, label: 'Scan Again', hint: 'Retry the search after registering' }]
|
|
151
|
+
: []),
|
|
152
|
+
...(discovery.status === 'error'
|
|
153
|
+
? [
|
|
154
|
+
{ value: 'retry' as DomainAction, label: errorMessage ? 'Try Again' : 'Retry Lookup', hint: 'Retry root ENS name search' },
|
|
155
|
+
{ value: 'manual' as DomainAction, label: 'Enter ENS Name Manually', hint: 'Lookup failed; type a root .eth name you own' },
|
|
156
|
+
]
|
|
157
|
+
: []),
|
|
158
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
159
|
+
{ value: 'back', label: 'Back', hint: 'Return to setup type', role: 'utility' },
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<Surface
|
|
164
|
+
title="Assign ENS Name"
|
|
165
|
+
subtitle="Choose a root .eth name, then create a dedicated agent subdomain under it."
|
|
166
|
+
footer={footerHint('enter select · esc back')}
|
|
167
|
+
>
|
|
168
|
+
<EnsStatusBanner identity={identity} noRootEnsName={noOwnedNames} />
|
|
169
|
+
{validationError ? <Text color={theme.accentError}>{validationError}</Text> : null}
|
|
170
|
+
{errorMessage ? <Text color={theme.accentError}>{errorMessage}: {discovery.status === 'error' ? discovery.message : ''}</Text> : null}
|
|
171
|
+
{warningMessage ? <Text color={theme.accentPeriwinkle}>{warningMessage}</Text> : null}
|
|
172
|
+
<Box marginTop={1}>
|
|
173
|
+
<Select<DomainAction>
|
|
174
|
+
options={options}
|
|
175
|
+
hintLayout="inline"
|
|
176
|
+
onSubmit={choice => {
|
|
177
|
+
if (choice === 'back') return setPhase({ kind: 'mode-select' })
|
|
178
|
+
if (choice === 'manual') { setPhase({ kind: 'manual-parent' }); return }
|
|
179
|
+
if (choice === 'register-root') { setPhase({ kind: 'register-root-input' }); return }
|
|
180
|
+
if (choice === 'retry') {
|
|
181
|
+
runDiscovery()
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
if (choice.startsWith('pick:')) {
|
|
185
|
+
const name = choice.slice('pick:'.length)
|
|
186
|
+
if (name) setPhase({ kind: 'pick-subdomain', parent: name })
|
|
187
|
+
}
|
|
188
|
+
}}
|
|
189
|
+
onCancel={() => setPhase({ kind: 'mode-select' })}
|
|
190
|
+
/>
|
|
191
|
+
</Box>
|
|
192
|
+
</Surface>
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (phase.kind === 'register-root-input') {
|
|
197
|
+
return (
|
|
198
|
+
<Surface
|
|
199
|
+
title="Register an ENS Name"
|
|
200
|
+
subtitle="Pick a name to register on Ethereum mainnet for 1 year. The connected wallet pays and becomes the owner."
|
|
201
|
+
footer={footerHint('enter continues · esc back')}
|
|
202
|
+
>
|
|
203
|
+
{phase.error ? <Text color={theme.accentError}>{phase.error}</Text> : null}
|
|
204
|
+
<Box marginTop={1}>
|
|
205
|
+
<TextInput
|
|
206
|
+
placeholder="myname"
|
|
207
|
+
validate={value => {
|
|
208
|
+
const result = validateRegistrableName(value)
|
|
209
|
+
return result.ok ? null : result.detail
|
|
210
|
+
}}
|
|
211
|
+
onSubmit={value => {
|
|
212
|
+
const result = validateRegistrableName(value)
|
|
213
|
+
if (!result.ok) {
|
|
214
|
+
setPhase({ kind: 'register-root-input', error: result.detail })
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
const secret = generateRegistrationSecret()
|
|
218
|
+
setPhase({ kind: 'register-root-pricing', label: result.label, secret, busy: true })
|
|
219
|
+
const client = createMainnetClient()
|
|
220
|
+
Promise.all([
|
|
221
|
+
readNameAvailable(client, result.label),
|
|
222
|
+
readRentPrice(client, result.label, BigInt(ONE_YEAR_SECONDS)),
|
|
223
|
+
])
|
|
224
|
+
.then(([available, price]) => {
|
|
225
|
+
if (!available) {
|
|
226
|
+
setPhase({ kind: 'register-root-input', error: `${result.label}.eth is already registered. Pick a different name.` })
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
setPhase({ kind: 'register-root-pricing', label: result.label, secret, busy: false, price })
|
|
230
|
+
})
|
|
231
|
+
.catch((err: unknown) => {
|
|
232
|
+
setPhase({ kind: 'register-root-input', error: err instanceof Error ? err.message : String(err) })
|
|
233
|
+
})
|
|
234
|
+
}}
|
|
235
|
+
onCancel={() => setPhase({ kind: 'pick-parent' })}
|
|
236
|
+
/>
|
|
237
|
+
</Box>
|
|
238
|
+
</Surface>
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (phase.kind === 'register-root-pricing') {
|
|
243
|
+
const fullName = `${phase.label}.eth`
|
|
244
|
+
if (phase.busy || !phase.price) {
|
|
245
|
+
return (
|
|
246
|
+
<Surface
|
|
247
|
+
title="Register an ENS Name"
|
|
248
|
+
subtitle={`Checking availability and price for ${fullName}...`}
|
|
249
|
+
footer={footerHint('esc back')}
|
|
250
|
+
>
|
|
251
|
+
<Box marginTop={1}><Text color={theme.textSubtle}>Reading from Ethereum mainnet...</Text></Box>
|
|
252
|
+
</Surface>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
const totalEth = formatEther(phase.price.total)
|
|
256
|
+
const baseEth = formatEther(phase.price.base)
|
|
257
|
+
const premiumEth = formatEther(phase.price.premium)
|
|
258
|
+
return (
|
|
259
|
+
<Surface
|
|
260
|
+
title="Register an ENS Name"
|
|
261
|
+
subtitle={`${fullName} is available. Review the cost and continue with two transactions.`}
|
|
262
|
+
footer={footerHint('enter select · esc back')}
|
|
263
|
+
>
|
|
264
|
+
<Box flexDirection="column">
|
|
265
|
+
<EnsSetupRow label="Name" value={fullName} />
|
|
266
|
+
<EnsSetupRow label="Owner" value={shortAddress(ownerAddress)} />
|
|
267
|
+
<EnsSetupRow label="Duration" value="1 year" />
|
|
268
|
+
<EnsSetupRow label="Rent" value={`${baseEth} ETH`} />
|
|
269
|
+
{phase.price.premium > 0n ? <EnsSetupRow label="Premium" value={`${premiumEth} ETH`} /> : null}
|
|
270
|
+
<EnsSetupRow label="Total" value={`${totalEth} ETH`} />
|
|
271
|
+
<Box marginTop={1}><Text color={theme.dim}>Two transactions: a commit, a 60-second wait, then the registration. Keep this tab open through both.</Text></Box>
|
|
272
|
+
<Box marginTop={1}><Text color={theme.dim}>Manage renewals on https://app.ens.domains/ before the year is up.</Text></Box>
|
|
273
|
+
</Box>
|
|
274
|
+
<Box marginTop={1}>
|
|
275
|
+
<Select<'commit' | 'pick-different' | 'back'>
|
|
276
|
+
options={[
|
|
277
|
+
{ value: 'commit', role: 'section', label: 'Continue' },
|
|
278
|
+
{ value: 'commit', label: 'Continue to Commit', hint: 'Sign the first transaction' },
|
|
279
|
+
{ value: 'pick-different', label: 'Pick a Different Name', hint: 'Return to name input' },
|
|
280
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
281
|
+
{ value: 'back', label: 'Back', hint: 'Return to root selection', role: 'utility' },
|
|
282
|
+
]}
|
|
283
|
+
hintLayout="inline"
|
|
284
|
+
onSubmit={choice => {
|
|
285
|
+
if (choice === 'commit') {
|
|
286
|
+
setPhase({ kind: 'register-root-commit-tx', label: phase.label, secret: phase.secret, price: phase.price! })
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
if (choice === 'pick-different') return setPhase({ kind: 'register-root-input' })
|
|
290
|
+
setPhase({ kind: 'pick-parent' })
|
|
291
|
+
}}
|
|
292
|
+
onCancel={() => setPhase({ kind: 'pick-parent' })}
|
|
293
|
+
/>
|
|
294
|
+
</Box>
|
|
295
|
+
</Surface>
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (phase.kind === 'register-root-commit-tx') {
|
|
300
|
+
return (
|
|
301
|
+
<RegisterRootCommitRunner
|
|
302
|
+
phase={phase}
|
|
303
|
+
ownerAddress={ownerAddress}
|
|
304
|
+
walletSession={operatorWalletSession}
|
|
305
|
+
onWalletReady={setOperatorWalletSession}
|
|
306
|
+
onCommitted={() => setPhase({ kind: 'register-root-wait', label: phase.label, secret: phase.secret, price: phase.price, commitMinedAt: Date.now() })}
|
|
307
|
+
onError={msg => setPhase({ kind: 'register-root-input', error: msg })}
|
|
308
|
+
/>
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (phase.kind === 'register-root-wait') {
|
|
313
|
+
return (
|
|
314
|
+
<RegisterRootWaitScreen
|
|
315
|
+
phase={phase}
|
|
316
|
+
onReady={() => setPhase({ kind: 'register-root-tx', label: phase.label, secret: phase.secret, price: phase.price })}
|
|
317
|
+
onCancel={() => setPhase({ kind: 'register-root-input', error: 'Commit cancelled. Names must be re-committed; secrets do not carry over.' })}
|
|
318
|
+
/>
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (phase.kind === 'register-root-tx') {
|
|
323
|
+
return (
|
|
324
|
+
<RegisterRootTxRunner
|
|
325
|
+
phase={phase}
|
|
326
|
+
ownerAddress={ownerAddress}
|
|
327
|
+
walletSession={operatorWalletSession}
|
|
328
|
+
onWalletReady={setOperatorWalletSession}
|
|
329
|
+
onRegistered={() => setPhase({ kind: 'register-root-done', fullName: `${phase.label}.eth` })}
|
|
330
|
+
onError={msg => setPhase({ kind: 'register-root-input', error: msg })}
|
|
331
|
+
/>
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (phase.kind === 'register-root-done') {
|
|
336
|
+
return (
|
|
337
|
+
<Surface
|
|
338
|
+
title="ENS Name Registered"
|
|
339
|
+
subtitle={`${phase.fullName} is now owned by ${shortAddress(ownerAddress)}.`}
|
|
340
|
+
footer={footerHint('enter select · esc back')}
|
|
341
|
+
>
|
|
342
|
+
<Box flexDirection="column">
|
|
343
|
+
<Text color={theme.text}>Manage renewals and other settings on https://app.ens.domains/ before the year is up.</Text>
|
|
344
|
+
<Text color={theme.dim}>The name will appear in the picker on the next scan.</Text>
|
|
345
|
+
</Box>
|
|
346
|
+
<Box marginTop={1}>
|
|
347
|
+
<Select<'scan' | 'back'>
|
|
348
|
+
options={[
|
|
349
|
+
{ value: 'scan', role: 'section', label: 'Next' },
|
|
350
|
+
{ value: 'scan', label: 'Scan Again', hint: 'Re-run root ENS name discovery' },
|
|
351
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
352
|
+
{ value: 'back', label: 'Back to ENS', hint: 'Return to ENS menu', role: 'utility' },
|
|
353
|
+
]}
|
|
354
|
+
hintLayout="inline"
|
|
355
|
+
onSubmit={choice => {
|
|
356
|
+
if (choice === 'scan') {
|
|
357
|
+
runDiscovery()
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
setPhase({ kind: 'mode-select' })
|
|
361
|
+
}}
|
|
362
|
+
onCancel={() => setPhase({ kind: 'mode-select' })}
|
|
363
|
+
/>
|
|
364
|
+
</Box>
|
|
365
|
+
</Surface>
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (phase.kind === 'manual-parent') {
|
|
370
|
+
return (
|
|
371
|
+
<Surface
|
|
372
|
+
title="Your Root .eth Name"
|
|
373
|
+
subtitle="Type the root .eth name you own. Your agent becomes a subdomain of it."
|
|
374
|
+
footer={footerHint('enter continues · esc back')}
|
|
375
|
+
>
|
|
376
|
+
{statusBanner}
|
|
377
|
+
<Box marginTop={1}>
|
|
378
|
+
<Text color={theme.dim}>Only root .eth names on Ethereum mainnet are supported.</Text>
|
|
379
|
+
</Box>
|
|
380
|
+
<TextInput
|
|
381
|
+
key="edit-ens-parent-manual"
|
|
382
|
+
placeholder="e.g. name.eth"
|
|
383
|
+
validate={value => {
|
|
384
|
+
const v = normalizeEthDomain(value)
|
|
385
|
+
if (!v) return 'Enter a .eth name'
|
|
386
|
+
if (!isEthDomain(v)) return 'Must be a valid .eth name'
|
|
387
|
+
if (!isRootEthName(v)) return 'Enter a root .eth name, e.g. name.eth'
|
|
388
|
+
return null
|
|
389
|
+
}}
|
|
390
|
+
onSubmit={value => setPhase({ kind: 'pick-subdomain', parent: normalizeEthDomain(value) })}
|
|
391
|
+
onCancel={() => setPhase({ kind: 'pick-parent' })}
|
|
392
|
+
/>
|
|
393
|
+
</Surface>
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (phase.kind === 'pick-subdomain') {
|
|
398
|
+
return (
|
|
399
|
+
<SubdomainEntry
|
|
400
|
+
parent={phase.parent}
|
|
401
|
+
ownerAddress={ownerAddress}
|
|
402
|
+
suggestion={phase.label || agentNameSuggestion}
|
|
403
|
+
error={phase.error}
|
|
404
|
+
onConfirm={fullName => { void runValidation(fullName, 'simple') }}
|
|
405
|
+
onBack={() => setPhase({ kind: 'pick-parent' })}
|
|
406
|
+
/>
|
|
407
|
+
)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (phase.kind === 'validating') {
|
|
411
|
+
return (
|
|
412
|
+
<Surface
|
|
413
|
+
title="Check ENS Name"
|
|
414
|
+
subtitle={phase.mode === 'simple' ? undefined : `Verifying ${phase.fullName}`}
|
|
415
|
+
footer={footerHint('esc cancels')}
|
|
416
|
+
>
|
|
417
|
+
<Box marginTop={1}>
|
|
418
|
+
<Spinner label="Looking up resolver and address record..." />
|
|
419
|
+
</Box>
|
|
420
|
+
<EscCancel onCancel={() => backToSimpleSubdomain(phase.fullName)} />
|
|
421
|
+
</Surface>
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (phase.kind === 'simple-name-missing') {
|
|
426
|
+
return (
|
|
427
|
+
<SimpleEnsIssueScreen
|
|
428
|
+
fullName={phase.fullName}
|
|
429
|
+
validation={phase.validation}
|
|
430
|
+
onCreate={() => runSimpleCreatePreflight(phase.fullName)}
|
|
431
|
+
onCheckAgain={() => { void runValidation(phase.fullName, 'simple') }}
|
|
432
|
+
onChange={() => backToSimpleSubdomain(phase.fullName)}
|
|
433
|
+
onBack={() => backToSimpleSubdomain(phase.fullName)}
|
|
434
|
+
/>
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (phase.kind === 'simple-create-preflight') {
|
|
439
|
+
return (
|
|
440
|
+
<Surface
|
|
441
|
+
title="Prepare ENS Name"
|
|
442
|
+
footer={footerHint('esc back')}
|
|
443
|
+
>
|
|
444
|
+
<Box flexDirection="column">
|
|
445
|
+
<Text color={theme.dim}>Name: <Text color={theme.text}>{phase.fullName}</Text></Text>
|
|
446
|
+
<Text color={theme.dim}>Connected wallet will create the subdomain and set the required records if checks pass.</Text>
|
|
447
|
+
</Box>
|
|
448
|
+
<Box marginTop={1}>
|
|
449
|
+
<Spinner label="checking parent ownership and record changes..." />
|
|
450
|
+
</Box>
|
|
451
|
+
<EscCancel onCancel={() => backToSimpleSubdomain(phase.fullName)} />
|
|
452
|
+
</Surface>
|
|
453
|
+
)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (phase.kind === 'simple-create-review') {
|
|
457
|
+
return (
|
|
458
|
+
<EnsSetupReviewScreen
|
|
459
|
+
setup={phase.setup}
|
|
460
|
+
currentEnsName={currentEnsName}
|
|
461
|
+
currentMode={savedCustodyMode}
|
|
462
|
+
registry={registry}
|
|
463
|
+
onBegin={() => {
|
|
464
|
+
if (phase.setup.txCount > 0) {
|
|
465
|
+
onEnsSetup(phase.setup)
|
|
466
|
+
return
|
|
467
|
+
}
|
|
468
|
+
onEnsLink(phase.setup.fullName, { mode: 'simple' })
|
|
469
|
+
}}
|
|
470
|
+
onBack={() => backToSimpleSubdomain(phase.setup.fullName)}
|
|
471
|
+
/>
|
|
472
|
+
)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (phase.kind === 'simple-create-blocked') {
|
|
476
|
+
return (
|
|
477
|
+
<EnsSetupBlockedScreen
|
|
478
|
+
fallback={phase.fallback}
|
|
479
|
+
onCheckAgain={() => runSimpleCreatePreflight(phase.fallback.fullName)}
|
|
480
|
+
onBack={() => backToSimpleSubdomain(phase.fallback.fullName)}
|
|
481
|
+
/>
|
|
482
|
+
)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (phase.kind === 'review') {
|
|
486
|
+
const linkOptions: EnsLinkOptions = phase.mode === 'advanced' && phase.ownerAddress && phase.operatorWallet
|
|
487
|
+
? { mode: 'advanced', ownerAddress: phase.ownerAddress, operatorWallet: phase.operatorWallet }
|
|
488
|
+
: { mode: 'simple' }
|
|
489
|
+
return (
|
|
490
|
+
<ReviewScreen
|
|
491
|
+
fullName={phase.fullName}
|
|
492
|
+
ownerAddress={ownerAddress}
|
|
493
|
+
validation={phase.validation}
|
|
494
|
+
recordsDiff={phase.recordsDiff}
|
|
495
|
+
nextRecords={phase.nextRecords}
|
|
496
|
+
currentEnsName={currentEnsName}
|
|
497
|
+
currentMode={savedCustodyMode}
|
|
498
|
+
registryNetworkLabel={registryNetworkLabel}
|
|
499
|
+
mode={phase.mode}
|
|
500
|
+
onContinue={() => {
|
|
501
|
+
if (recordsDiffHasChanges(phase.recordsDiff)) {
|
|
502
|
+
onEnsRecordsUpdate(phase.fullName, phase.nextRecords, linkOptions, false, phase.currentRecords)
|
|
503
|
+
return
|
|
504
|
+
}
|
|
505
|
+
onEnsLink(phase.fullName, linkOptions)
|
|
506
|
+
}}
|
|
507
|
+
onCheckAgain={() => { void runValidation(phase.fullName, phase.mode, phase.ownerAddress, phase.operatorWallet) }}
|
|
508
|
+
onChange={() => setPhase(phase.mode === 'advanced' ? { kind: 'advanced-transfer-check' } : { kind: 'pick-parent' })}
|
|
509
|
+
onCreate={phase.mode === 'simple' && !phase.validation.ok && phase.validation.reason === 'no-owner'
|
|
510
|
+
? () => runSimpleCreatePreflight(phase.fullName)
|
|
511
|
+
: undefined}
|
|
512
|
+
onBack={() => backToSimpleSubdomain(phase.fullName)}
|
|
513
|
+
/>
|
|
514
|
+
)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return null
|
|
518
|
+
}
|