ethagent 1.1.1 → 2.0.0
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 +127 -29
- package/package.json +16 -9
- package/src/app/FirstRun.tsx +192 -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 +43 -18
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +11 -17
- package/src/chat/ConversationStack.tsx +3 -0
- 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/TranscriptView.tsx +6 -0
- 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 +5 -3
- 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 -815
- 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/flows/continuity/RecoveryConfirmScreen.tsx +104 -0
- 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 +25 -43
- 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 +166 -101
- 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 +21 -9
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +3 -3
- package/src/ui/TextInput.tsx +191 -29
- 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 -291
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -144
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -145
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -90
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +0 -87
- 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,471 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { getAddress, type Address } from 'viem'
|
|
4
|
+
import { Surface } from '../../../../ui/Surface.js'
|
|
5
|
+
import { Select, type SelectOption } from '../../../../ui/Select.js'
|
|
6
|
+
import { theme } from '../../../../ui/theme.js'
|
|
7
|
+
import {
|
|
8
|
+
formatRecordValue,
|
|
9
|
+
recordLabel,
|
|
10
|
+
type AgentEnsRecords,
|
|
11
|
+
type AgentRecordDiff,
|
|
12
|
+
} from '../../../ens/agentRecords.js'
|
|
13
|
+
import type { EnsValidation } from '../../../ens/ensLookup.js'
|
|
14
|
+
import type {
|
|
15
|
+
EnsSetupBlockedPlan,
|
|
16
|
+
EnsSetupPlan,
|
|
17
|
+
} from '../../../ens/ensAutomation.js'
|
|
18
|
+
import { createErc8004PublicClient, type Erc8004RegistryConfig } from '../../../registry/erc8004.js'
|
|
19
|
+
import {
|
|
20
|
+
displayCustodyMode,
|
|
21
|
+
type CustodyMode,
|
|
22
|
+
} from '../../model/custody.js'
|
|
23
|
+
import { ensValidationReasonText } from '../../model/ens.js'
|
|
24
|
+
import { shortAddress } from '../../model/format.js'
|
|
25
|
+
import {
|
|
26
|
+
manualReasonTitle,
|
|
27
|
+
modeSwitchHeading,
|
|
28
|
+
setupSwitchNotice,
|
|
29
|
+
} from './ensEditCopy.js'
|
|
30
|
+
import {
|
|
31
|
+
EnsSetupRow,
|
|
32
|
+
footerHint,
|
|
33
|
+
renderRecordValue,
|
|
34
|
+
} from './EnsEditShared.js'
|
|
35
|
+
import type { EnsIssueValidation } from './ensEditTypes.js'
|
|
36
|
+
|
|
37
|
+
type SimpleEnsIssueScreenProps = {
|
|
38
|
+
fullName: string
|
|
39
|
+
validation: EnsIssueValidation
|
|
40
|
+
onCreate: () => void
|
|
41
|
+
onCheckAgain: () => void
|
|
42
|
+
onChange: () => void
|
|
43
|
+
onBack: () => void
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const SimpleEnsIssueScreen: React.FC<SimpleEnsIssueScreenProps> = ({
|
|
47
|
+
fullName,
|
|
48
|
+
validation,
|
|
49
|
+
onCreate,
|
|
50
|
+
onCheckAgain,
|
|
51
|
+
onChange,
|
|
52
|
+
onBack,
|
|
53
|
+
}) => {
|
|
54
|
+
type Action = 'create' | 'check-again' | 'change' | 'back'
|
|
55
|
+
const reason = ensValidationReasonText(validation.reason)
|
|
56
|
+
const showDetail = validation.detail && validation.detail !== reason
|
|
57
|
+
return (
|
|
58
|
+
<Surface
|
|
59
|
+
title="ENS Name Not Found"
|
|
60
|
+
footer={footerHint('enter select · esc back')}
|
|
61
|
+
>
|
|
62
|
+
<Box flexDirection="column">
|
|
63
|
+
<Text color={theme.dim}>The subdomain is not on Ethereum Mainnet yet. You can create it from here.</Text>
|
|
64
|
+
<Text>
|
|
65
|
+
<Text color={theme.dim}>{'Name'.padEnd(12)}</Text>
|
|
66
|
+
<Text color={theme.text} bold>{fullName}</Text>
|
|
67
|
+
</Text>
|
|
68
|
+
<Text>
|
|
69
|
+
<Text color={theme.dim}>{'Reason'.padEnd(12)}</Text>
|
|
70
|
+
<Text color={theme.accentError}>{reason}</Text>
|
|
71
|
+
</Text>
|
|
72
|
+
{showDetail ? <Text color={theme.dim}>{validation.detail}</Text> : null}
|
|
73
|
+
</Box>
|
|
74
|
+
<Box marginTop={1}>
|
|
75
|
+
<Select<Action>
|
|
76
|
+
options={[
|
|
77
|
+
{ value: 'create', role: 'section', label: 'Create Subdomain' },
|
|
78
|
+
{ value: 'create', label: 'Create This ENS Name', hint: 'Connected wallet creates it and sets the required records' },
|
|
79
|
+
{ value: 'change', label: 'Pick A Different Name', hint: 'Return to subdomain entry with this name still filled in' },
|
|
80
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
81
|
+
{ value: 'back', label: 'Back', hint: 'Return to subdomain entry', role: 'utility' },
|
|
82
|
+
]}
|
|
83
|
+
hintLayout="inline"
|
|
84
|
+
onSubmit={choice => {
|
|
85
|
+
if (choice === 'create') return onCreate()
|
|
86
|
+
if (choice === 'check-again') return onCheckAgain()
|
|
87
|
+
if (choice === 'change') return onChange()
|
|
88
|
+
return onBack()
|
|
89
|
+
}}
|
|
90
|
+
onCancel={onBack}
|
|
91
|
+
/>
|
|
92
|
+
</Box>
|
|
93
|
+
</Surface>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
type EnsSetupReviewScreenProps = {
|
|
98
|
+
setup: EnsSetupPlan
|
|
99
|
+
currentEnsName: string
|
|
100
|
+
currentMode: CustodyMode | undefined
|
|
101
|
+
registry: Erc8004RegistryConfig
|
|
102
|
+
onBegin: () => void
|
|
103
|
+
onBack: () => void
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const EnsSetupReviewScreen: React.FC<EnsSetupReviewScreenProps> = ({
|
|
107
|
+
setup,
|
|
108
|
+
currentEnsName,
|
|
109
|
+
currentMode,
|
|
110
|
+
registry,
|
|
111
|
+
onBegin,
|
|
112
|
+
onBack,
|
|
113
|
+
}) => {
|
|
114
|
+
type Action = 'begin' | 'back'
|
|
115
|
+
const isSimple = setup.mode === 'simple'
|
|
116
|
+
const [ownerIsSmartAccount, setOwnerIsSmartAccount] = React.useState(false)
|
|
117
|
+
React.useEffect(() => {
|
|
118
|
+
if (isSimple) return
|
|
119
|
+
let cancelled = false
|
|
120
|
+
const client = createErc8004PublicClient(registry)
|
|
121
|
+
client.getBytecode({ address: getAddress(setup.ownerAddress) })
|
|
122
|
+
.then(code => {
|
|
123
|
+
if (cancelled) return
|
|
124
|
+
if (code && code !== '0x') setOwnerIsSmartAccount(true)
|
|
125
|
+
})
|
|
126
|
+
.catch(() => {})
|
|
127
|
+
return () => { cancelled = true }
|
|
128
|
+
}, [isSimple, registry, setup.ownerAddress])
|
|
129
|
+
const signerLabel = isSimple ? 'Connected wallet' : 'Owner wallet'
|
|
130
|
+
const switchNotice = setupSwitchNotice(currentEnsName, currentMode, setup.fullName, setup.mode)
|
|
131
|
+
const createLabel = setup.registryAction === 'create-subdomain'
|
|
132
|
+
? 'Create Subdomain'
|
|
133
|
+
: setup.registryAction === 'create-wrapped-subdomain'
|
|
134
|
+
? 'Create Wrapped Subdomain'
|
|
135
|
+
: setup.registryAction === 'set-resolver'
|
|
136
|
+
? 'Set Resolver'
|
|
137
|
+
: setup.registryAction === 'set-wrapped-resolver'
|
|
138
|
+
? 'Set Wrapped Resolver'
|
|
139
|
+
: 'Subdomain Ready'
|
|
140
|
+
const beginHint = setup.registryAction === 'none'
|
|
141
|
+
? 'Ethereum Mainnet: subdomain already ready'
|
|
142
|
+
: 'Ethereum Mainnet: create or prepare subdomain'
|
|
143
|
+
const reusingExistingSubdomain = setup.registryAction === 'none'
|
|
144
|
+
return (
|
|
145
|
+
<Surface
|
|
146
|
+
title={isSimple ? 'Create Simple ENS Name' : 'Create ENS Name'}
|
|
147
|
+
footer={footerHint('enter select · esc back')}
|
|
148
|
+
>
|
|
149
|
+
{reusingExistingSubdomain ? (
|
|
150
|
+
<Box marginBottom={1}>
|
|
151
|
+
<Text color={theme.accentPeriwinkle}>Subdomain detected from a prior attempt, reusing.</Text>
|
|
152
|
+
</Box>
|
|
153
|
+
) : null}
|
|
154
|
+
<Box flexDirection="column">
|
|
155
|
+
<Text color={theme.dim}>{modeSwitchHeading(currentEnsName, currentMode, setup.fullName, setup.mode)}</Text>
|
|
156
|
+
{switchNotice ? <Text color={theme.dim}>{switchNotice}</Text> : null}
|
|
157
|
+
<EnsSetupRow label="ENS name" value={setup.fullName} />
|
|
158
|
+
<EnsSetupRow label="Parent root" value={setup.rootName} />
|
|
159
|
+
<EnsSetupRow label="Subdomain label" value={setup.label} />
|
|
160
|
+
<EnsSetupRow label="ENS network" value="Ethereum Mainnet" />
|
|
161
|
+
<EnsSetupRow label="Signer wallet" value={`${shortAddress(setup.ownerAddress)} (${signerLabel.toLowerCase()})`} />
|
|
162
|
+
<EnsSetupRow label="Registry action" value={createLabel} />
|
|
163
|
+
{ownerIsSmartAccount ? (
|
|
164
|
+
<Box marginTop={1}>
|
|
165
|
+
<Text color={theme.accentError}>Owner wallet appears to be a smart account. Resolver delegation may require special handling depending on your wallet provider; if the operator wallet later cannot write ENS records, run "Fix Records" to retry.</Text>
|
|
166
|
+
</Box>
|
|
167
|
+
) : null}
|
|
168
|
+
</Box>
|
|
169
|
+
<Box marginTop={1}>
|
|
170
|
+
<Select<Action>
|
|
171
|
+
options={[
|
|
172
|
+
{ value: 'begin', role: 'section', label: 'Subdomain' },
|
|
173
|
+
{ value: 'begin', label: 'Continue Setup', hint: beginHint },
|
|
174
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
175
|
+
{ value: 'back', label: 'Back', hint: isSimple ? 'Return to subdomain entry' : 'Return to operator wallet', role: 'utility' },
|
|
176
|
+
]}
|
|
177
|
+
hintLayout="inline"
|
|
178
|
+
onSubmit={choice => {
|
|
179
|
+
if (choice === 'begin') return onBegin()
|
|
180
|
+
return onBack()
|
|
181
|
+
}}
|
|
182
|
+
onCancel={onBack}
|
|
183
|
+
/>
|
|
184
|
+
</Box>
|
|
185
|
+
</Surface>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
type EnsSetupBlockedScreenProps = {
|
|
190
|
+
fallback: EnsSetupBlockedPlan
|
|
191
|
+
onCheckAgain: () => void
|
|
192
|
+
onBack: () => void
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export const EnsSetupBlockedScreen: React.FC<EnsSetupBlockedScreenProps> = ({
|
|
196
|
+
fallback,
|
|
197
|
+
onCheckAgain,
|
|
198
|
+
onBack,
|
|
199
|
+
}) => {
|
|
200
|
+
type Action = 'check' | 'back'
|
|
201
|
+
const isSimple = fallback.mode === 'simple'
|
|
202
|
+
return (
|
|
203
|
+
<Surface
|
|
204
|
+
title="ENS Setup Blocked"
|
|
205
|
+
footer={footerHint('enter select · esc back')}
|
|
206
|
+
>
|
|
207
|
+
<Box flexDirection="column">
|
|
208
|
+
<Text color={theme.accentError}>{manualReasonTitle(fallback.reason)}</Text>
|
|
209
|
+
<Text color={theme.dim}>{fallback.detail}</Text>
|
|
210
|
+
<Box marginTop={1} flexDirection="column">
|
|
211
|
+
<EnsSetupRow label="Agent ENS" value={fallback.fullName} />
|
|
212
|
+
{isSimple
|
|
213
|
+
? <EnsSetupRow label="Wallet" value={fallback.ownerAddress ? shortAddress(fallback.ownerAddress) : shortAddress(fallback.operatorAddress)} />
|
|
214
|
+
: (fallback.ownerAddress ? <EnsSetupRow label="Owner wallet" value={shortAddress(fallback.ownerAddress)} /> : null)}
|
|
215
|
+
{fallback.nextRecords?.token ? <EnsSetupRow label="Token link" value={fallback.nextRecords.token} /> : null}
|
|
216
|
+
<EnsSetupRow label="Address" value={`Set the subdomain address record to the ${isSimple ? 'connected wallet' : 'owner wallet'}.`} />
|
|
217
|
+
{!isSimple
|
|
218
|
+
? (
|
|
219
|
+
<>
|
|
220
|
+
<Box marginTop={1} flexDirection="column">
|
|
221
|
+
<Text color={theme.text}>To proceed: the owner wallet signs ENS records and must hold this token at setup time. Once setup is done you can deposit the token into the operator delegation vault while the ENS subdomain stays with the owner wallet.</Text>
|
|
222
|
+
</Box>
|
|
223
|
+
<Text color={theme.dim}>Operator wallets have no authority on this name; they only rotate the onchain ERC-8004 URI via the operator delegation vault.</Text>
|
|
224
|
+
</>
|
|
225
|
+
)
|
|
226
|
+
: null}
|
|
227
|
+
</Box>
|
|
228
|
+
</Box>
|
|
229
|
+
<Box marginTop={1}>
|
|
230
|
+
<Select<Action>
|
|
231
|
+
options={[
|
|
232
|
+
{ value: 'check', role: 'section', label: 'Automation' },
|
|
233
|
+
{ value: 'check', label: 'Check Again', hint: isSimple ? 'Re-run the simple ENS automation check' : 'Re-read ENS after the token is back with the owner wallet' },
|
|
234
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
235
|
+
{ value: 'back', label: 'Back', hint: isSimple ? 'Return to subdomain entry' : 'Return to the previous setup step', role: 'utility' },
|
|
236
|
+
]}
|
|
237
|
+
hintLayout="inline"
|
|
238
|
+
onSubmit={choice => {
|
|
239
|
+
if (choice === 'check') return onCheckAgain()
|
|
240
|
+
return onBack()
|
|
241
|
+
}}
|
|
242
|
+
onCancel={onBack}
|
|
243
|
+
/>
|
|
244
|
+
</Box>
|
|
245
|
+
</Surface>
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
type UnlinkEnsReviewScreenProps = {
|
|
250
|
+
fullName: string
|
|
251
|
+
currentMode: CustodyMode | undefined
|
|
252
|
+
registryNetworkLabel: string
|
|
253
|
+
recordsDiff: AgentRecordDiff[]
|
|
254
|
+
onUnlink: () => void
|
|
255
|
+
onBack: () => void
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export const UnlinkEnsReviewScreen: React.FC<UnlinkEnsReviewScreenProps> = ({
|
|
259
|
+
fullName,
|
|
260
|
+
currentMode,
|
|
261
|
+
registryNetworkLabel,
|
|
262
|
+
recordsDiff,
|
|
263
|
+
onUnlink,
|
|
264
|
+
onBack,
|
|
265
|
+
}) => {
|
|
266
|
+
type Action = 'unlink' | 'back'
|
|
267
|
+
const changedDiffs = recordsDiff.filter(diff => diff.current.trim())
|
|
268
|
+
const recordsAlreadyEmpty = changedDiffs.length === 0
|
|
269
|
+
return (
|
|
270
|
+
<Surface
|
|
271
|
+
title="Unlink ENS"
|
|
272
|
+
footer={footerHint('enter select · esc back')}
|
|
273
|
+
>
|
|
274
|
+
<Box flexDirection="column">
|
|
275
|
+
<EnsSetupRow label="ENS name" value={fullName} />
|
|
276
|
+
<EnsSetupRow label="Custody" value={displayCustodyMode(currentMode)} />
|
|
277
|
+
<Text color={theme.dim}>
|
|
278
|
+
Records on Ethereum Mainnet, then token URI on {registryNetworkLabel}.
|
|
279
|
+
</Text>
|
|
280
|
+
{recordsAlreadyEmpty ? (
|
|
281
|
+
<Box marginTop={1}>
|
|
282
|
+
<Text color={theme.dim}>
|
|
283
|
+
All ethagent ENS records are already empty. Unlink removes only the token URI link.
|
|
284
|
+
</Text>
|
|
285
|
+
</Box>
|
|
286
|
+
) : (
|
|
287
|
+
<Box marginTop={1} flexDirection="column">
|
|
288
|
+
<Text color={theme.textSubtle}>Will be cleared:</Text>
|
|
289
|
+
{changedDiffs.map(diff => (
|
|
290
|
+
<Text key={diff.key}>
|
|
291
|
+
<Text color={theme.dim}>{` ${diff.key} `}</Text>
|
|
292
|
+
<Text color={theme.accentPeriwinkle}>{formatRecordValue(diff.field, diff.current)}</Text>
|
|
293
|
+
</Text>
|
|
294
|
+
))}
|
|
295
|
+
</Box>
|
|
296
|
+
)}
|
|
297
|
+
</Box>
|
|
298
|
+
<Box marginTop={1}>
|
|
299
|
+
<Select<Action>
|
|
300
|
+
options={[
|
|
301
|
+
{ value: 'unlink', role: 'section', label: 'Unlink' },
|
|
302
|
+
{
|
|
303
|
+
value: 'unlink',
|
|
304
|
+
label: 'Unlink ENS',
|
|
305
|
+
hint: recordsAlreadyEmpty
|
|
306
|
+
? 'Skip the records transaction and remove only the token URI link'
|
|
307
|
+
: 'Clear ethagent-owned records, then remove the token URI link',
|
|
308
|
+
},
|
|
309
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
310
|
+
{ value: 'back', label: 'Back', hint: 'Return to ENS setup', role: 'utility' },
|
|
311
|
+
]}
|
|
312
|
+
hintLayout="inline"
|
|
313
|
+
onSubmit={choice => {
|
|
314
|
+
if (choice === 'unlink') return onUnlink()
|
|
315
|
+
return onBack()
|
|
316
|
+
}}
|
|
317
|
+
onCancel={onBack}
|
|
318
|
+
/>
|
|
319
|
+
</Box>
|
|
320
|
+
</Surface>
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
type ReviewScreenProps = {
|
|
325
|
+
fullName: string
|
|
326
|
+
ownerAddress: Address
|
|
327
|
+
validation: EnsValidation
|
|
328
|
+
recordsDiff: AgentRecordDiff[]
|
|
329
|
+
nextRecords: AgentEnsRecords
|
|
330
|
+
currentEnsName: string
|
|
331
|
+
currentMode: CustodyMode | undefined
|
|
332
|
+
registryNetworkLabel: string
|
|
333
|
+
mode: 'simple' | 'advanced'
|
|
334
|
+
onContinue: () => void
|
|
335
|
+
onCheckAgain: () => void
|
|
336
|
+
onChange: () => void
|
|
337
|
+
onCreate?: () => void
|
|
338
|
+
onBack: () => void
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export const ReviewScreen: React.FC<ReviewScreenProps> = ({
|
|
342
|
+
fullName,
|
|
343
|
+
ownerAddress,
|
|
344
|
+
validation,
|
|
345
|
+
recordsDiff,
|
|
346
|
+
nextRecords,
|
|
347
|
+
currentEnsName,
|
|
348
|
+
currentMode,
|
|
349
|
+
registryNetworkLabel,
|
|
350
|
+
mode,
|
|
351
|
+
onContinue,
|
|
352
|
+
onCheckAgain,
|
|
353
|
+
onChange,
|
|
354
|
+
onCreate,
|
|
355
|
+
onBack,
|
|
356
|
+
}) => {
|
|
357
|
+
void ownerAddress
|
|
358
|
+
void nextRecords
|
|
359
|
+
type ReviewAction = 'continue' | 'create' | 'check-again' | 'change' | 'back'
|
|
360
|
+
const changedDiffs = recordsDiff.filter(d => d.changed)
|
|
361
|
+
const hasRecordChanges = changedDiffs.length > 0
|
|
362
|
+
const reviewSubtitle = 'Review Ethereum Mainnet ENS address and text records before saving this ENS link. No token approval is requested.'
|
|
363
|
+
const switchNotice = setupSwitchNotice(currentEnsName, currentMode, fullName, mode)
|
|
364
|
+
|
|
365
|
+
if (!validation.ok) {
|
|
366
|
+
const reason = ensValidationReasonText(validation.reason)
|
|
367
|
+
const showDetail = validation.detail && validation.detail !== reason
|
|
368
|
+
return (
|
|
369
|
+
<Surface
|
|
370
|
+
title="ENS Issue"
|
|
371
|
+
subtitle={`${fullName} could not be verified on Ethereum mainnet.`}
|
|
372
|
+
footer={footerHint('enter select · esc back')}
|
|
373
|
+
>
|
|
374
|
+
<Box flexDirection="column">
|
|
375
|
+
<Text>
|
|
376
|
+
<Text color={theme.dim}>{'Name'.padEnd(12)}</Text>
|
|
377
|
+
<Text color={theme.text} bold>{fullName}</Text>
|
|
378
|
+
</Text>
|
|
379
|
+
<Text>
|
|
380
|
+
<Text color={theme.dim}>{'Reason'.padEnd(12)}</Text>
|
|
381
|
+
<Text color={theme.accentError}>{reason}</Text>
|
|
382
|
+
</Text>
|
|
383
|
+
{showDetail
|
|
384
|
+
? <Text color={theme.dim}>{validation.detail}</Text>
|
|
385
|
+
: null}
|
|
386
|
+
</Box>
|
|
387
|
+
<Box marginTop={1}>
|
|
388
|
+
<Select<ReviewAction>
|
|
389
|
+
options={[
|
|
390
|
+
...(onCreate
|
|
391
|
+
? [
|
|
392
|
+
{ value: 'create' as ReviewAction, role: 'section' as const, label: 'Create Subdomain' },
|
|
393
|
+
{ value: 'create' as ReviewAction, label: 'Create This ENS Name', hint: 'Use ethagent to create it and set the required records' },
|
|
394
|
+
]
|
|
395
|
+
: []),
|
|
396
|
+
{ value: 'check-again', label: 'Check Again', hint: 'Re-verify on Ethereum mainnet' },
|
|
397
|
+
{ value: 'change', label: 'Pick A Different Name', hint: 'Return to the name picker' },
|
|
398
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
399
|
+
{ value: 'back', label: 'Back', hint: 'Return to subdomain entry', role: 'utility' },
|
|
400
|
+
]}
|
|
401
|
+
hintLayout="inline"
|
|
402
|
+
onSubmit={choice => {
|
|
403
|
+
if (choice === 'create' && onCreate) return onCreate()
|
|
404
|
+
if (choice === 'check-again') return onCheckAgain()
|
|
405
|
+
if (choice === 'change') return onChange()
|
|
406
|
+
return onBack()
|
|
407
|
+
}}
|
|
408
|
+
onCancel={onBack}
|
|
409
|
+
/>
|
|
410
|
+
</Box>
|
|
411
|
+
</Surface>
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const options: Array<SelectOption<ReviewAction>> = [
|
|
416
|
+
{ value: 'continue', role: 'section', label: modeSwitchHeading(currentEnsName, currentMode, fullName, mode) },
|
|
417
|
+
{ value: 'continue', label: 'Continue Setup', hint: hasRecordChanges ? `Ethereum Mainnet: sign ${changedDiffs.length} ENS record value${changedDiffs.length === 1 ? '' : 's'}; ${registryNetworkLabel}: save token URI` : `ENS records already match; ${registryNetworkLabel}: save token URI` },
|
|
418
|
+
{ value: 'change', label: 'Pick A Different Name', hint: 'Return to the name picker' },
|
|
419
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
420
|
+
{ value: 'back', label: 'Back', hint: 'Return to Identity Hub', role: 'utility' },
|
|
421
|
+
]
|
|
422
|
+
|
|
423
|
+
return (
|
|
424
|
+
<Surface
|
|
425
|
+
title={`Update ENS records for ${fullName}?`}
|
|
426
|
+
subtitle={reviewSubtitle}
|
|
427
|
+
footer={footerHint('enter select · esc back')}
|
|
428
|
+
>
|
|
429
|
+
<Box flexDirection="column">
|
|
430
|
+
{currentEnsName || currentMode
|
|
431
|
+
? (
|
|
432
|
+
<Box marginBottom={1} flexDirection="column">
|
|
433
|
+
<Text color={theme.dim}>Current: <Text color={currentEnsName ? theme.text : theme.dim}>{currentEnsName || 'None'}</Text></Text>
|
|
434
|
+
<Text color={theme.dim}>Next: <Text color={theme.text}>{fullName}</Text></Text>
|
|
435
|
+
{switchNotice ? <Text color={theme.dim}>{switchNotice}</Text> : null}
|
|
436
|
+
</Box>
|
|
437
|
+
)
|
|
438
|
+
: null}
|
|
439
|
+
{recordsDiff.map(diff => (
|
|
440
|
+
<Text key={diff.key}>
|
|
441
|
+
<Text color={theme.dim}>{`- ${recordLabel(diff.field)}: `}</Text>
|
|
442
|
+
{diff.changed
|
|
443
|
+
? (
|
|
444
|
+
<>
|
|
445
|
+
{renderRecordValue(diff.field, diff.current)}
|
|
446
|
+
<Text color={theme.dim}>{' → '}</Text>
|
|
447
|
+
{renderRecordValue(diff.field, diff.next)}
|
|
448
|
+
</>
|
|
449
|
+
)
|
|
450
|
+
: renderRecordValue(diff.field, diff.next)}
|
|
451
|
+
</Text>
|
|
452
|
+
))}
|
|
453
|
+
{!hasRecordChanges
|
|
454
|
+
? <Box marginTop={1}><Text color={theme.dim}>All ENS records already match. Link this ENS name to the token URI.</Text></Box>
|
|
455
|
+
: null}
|
|
456
|
+
</Box>
|
|
457
|
+
<Box marginTop={1}>
|
|
458
|
+
<Select<ReviewAction>
|
|
459
|
+
options={options}
|
|
460
|
+
hintLayout="inline"
|
|
461
|
+
onSubmit={choice => {
|
|
462
|
+
if (choice === 'continue') return onContinue()
|
|
463
|
+
if (choice === 'change') return onChange()
|
|
464
|
+
return onBack()
|
|
465
|
+
}}
|
|
466
|
+
onCancel={onBack}
|
|
467
|
+
/>
|
|
468
|
+
</Box>
|
|
469
|
+
</Surface>
|
|
470
|
+
)
|
|
471
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import type { Address } from 'viem'
|
|
4
|
+
import { mainnet } from 'viem/chains'
|
|
5
|
+
import { Surface } from '../../../../ui/Surface.js'
|
|
6
|
+
import { useAppInput } from '../../../../app/input/AppInputProvider.js'
|
|
7
|
+
import {
|
|
8
|
+
createMainnetClient,
|
|
9
|
+
} from '../../../ens/ensLookup.js'
|
|
10
|
+
import {
|
|
11
|
+
buildCommitment,
|
|
12
|
+
encodeCommitTransaction,
|
|
13
|
+
encodeRegisterTransaction,
|
|
14
|
+
MIN_COMMIT_AGE_SECONDS,
|
|
15
|
+
ONE_YEAR_SECONDS,
|
|
16
|
+
} from '../../../ens/ensRegistration.js'
|
|
17
|
+
import type { EnsSubdomainDeletePlan } from '../../../ens/ensAutomation.js'
|
|
18
|
+
import {
|
|
19
|
+
sendBrowserWalletTransaction,
|
|
20
|
+
type BrowserWalletReady,
|
|
21
|
+
} from '../../../wallet/browserWallet.js'
|
|
22
|
+
import { WalletApprovalScreen } from '../../components/WalletApprovalScreen.js'
|
|
23
|
+
import { footerHint } from './EnsEditShared.js'
|
|
24
|
+
import type { RegisterCommitPhase } from './ensEditTypes.js'
|
|
25
|
+
import { theme } from '../../../../ui/theme.js'
|
|
26
|
+
|
|
27
|
+
export const EscCancel: React.FC<{ onCancel: () => void }> = ({ onCancel }) => {
|
|
28
|
+
useAppInput((_input, key) => {
|
|
29
|
+
if (key.escape) onCancel()
|
|
30
|
+
})
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const RegisterRootCommitRunner: React.FC<{
|
|
35
|
+
phase: RegisterCommitPhase
|
|
36
|
+
ownerAddress: Address
|
|
37
|
+
walletSession: BrowserWalletReady | null
|
|
38
|
+
onWalletReady: (session: BrowserWalletReady | null) => void
|
|
39
|
+
onCommitted: () => void
|
|
40
|
+
onError: (msg: string) => void
|
|
41
|
+
}> = ({ phase, ownerAddress, walletSession, onWalletReady, onCommitted, onError }) => {
|
|
42
|
+
const startedRef = React.useRef(false)
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
if (startedRef.current) return
|
|
45
|
+
startedRef.current = true
|
|
46
|
+
const built = buildCommitment({ label: phase.label, owner: ownerAddress, durationSeconds: BigInt(ONE_YEAR_SECONDS), secret: phase.secret })
|
|
47
|
+
const tx = encodeCommitTransaction(built.commitment)
|
|
48
|
+
sendBrowserWalletTransaction({
|
|
49
|
+
chainId: mainnet.id,
|
|
50
|
+
expectedAccount: ownerAddress,
|
|
51
|
+
to: tx.to,
|
|
52
|
+
data: tx.data,
|
|
53
|
+
purpose: 'register-root-commit',
|
|
54
|
+
onReady: ready => onWalletReady(ready),
|
|
55
|
+
})
|
|
56
|
+
.then(async result => {
|
|
57
|
+
onWalletReady(null)
|
|
58
|
+
const client = createMainnetClient()
|
|
59
|
+
await client.waitForTransactionReceipt({ hash: result.txHash })
|
|
60
|
+
onCommitted()
|
|
61
|
+
})
|
|
62
|
+
.catch((err: unknown) => {
|
|
63
|
+
onWalletReady(null)
|
|
64
|
+
onError(err instanceof Error ? err.message : String(err))
|
|
65
|
+
})
|
|
66
|
+
}, [])
|
|
67
|
+
return (
|
|
68
|
+
<WalletApprovalScreen
|
|
69
|
+
title="Commit ENS Name"
|
|
70
|
+
subtitle={`Submitting the first of two transactions for ${phase.label}.eth on Ethereum mainnet. Wait for confirmation.`}
|
|
71
|
+
walletSession={walletSession}
|
|
72
|
+
label="waiting for connected wallet transaction..."
|
|
73
|
+
onCancel={() => onError('Commit cancelled. Restart from the name input.')}
|
|
74
|
+
/>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const RegisterRootWaitScreen: React.FC<{
|
|
79
|
+
phase: RegisterCommitPhase & { commitMinedAt: number }
|
|
80
|
+
onReady: () => void
|
|
81
|
+
onCancel: () => void
|
|
82
|
+
}> = ({ phase, onReady, onCancel }) => {
|
|
83
|
+
const [, setTick] = React.useState(0)
|
|
84
|
+
React.useEffect(() => {
|
|
85
|
+
const interval = setInterval(() => setTick(t => t + 1), 500)
|
|
86
|
+
return () => clearInterval(interval)
|
|
87
|
+
}, [])
|
|
88
|
+
const elapsedSec = Math.floor((Date.now() - phase.commitMinedAt) / 1000)
|
|
89
|
+
const remaining = Math.max(0, MIN_COMMIT_AGE_SECONDS + 1 - elapsedSec)
|
|
90
|
+
React.useEffect(() => {
|
|
91
|
+
if (remaining === 0) onReady()
|
|
92
|
+
}, [remaining])
|
|
93
|
+
return (
|
|
94
|
+
<Surface
|
|
95
|
+
title="Commit Confirmed, Waiting"
|
|
96
|
+
subtitle={`Anti-frontrun delay before registering ${phase.label}.eth. ENS requires a 60-second wait between commit and register.`}
|
|
97
|
+
footer={footerHint('esc cancel registration')}
|
|
98
|
+
>
|
|
99
|
+
<Box marginTop={1} flexDirection="column">
|
|
100
|
+
<Text color={theme.text}>{remaining > 0 ? `${remaining} seconds remaining...` : 'Ready, advancing to the registration transaction...'}</Text>
|
|
101
|
+
<Text color={theme.dim}>Keep this window open. The commit expires after 24 hours if you walk away.</Text>
|
|
102
|
+
</Box>
|
|
103
|
+
<EscCancel onCancel={onCancel} />
|
|
104
|
+
</Surface>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const DeleteSubdomainTxRunner: React.FC<{
|
|
109
|
+
plan: EnsSubdomainDeletePlan
|
|
110
|
+
ownerAddress: Address
|
|
111
|
+
walletSession: BrowserWalletReady | null
|
|
112
|
+
onWalletReady: (session: BrowserWalletReady | null) => void
|
|
113
|
+
onDeleted: () => void
|
|
114
|
+
onError: (msg: string) => void
|
|
115
|
+
}> = ({ plan, ownerAddress, walletSession, onWalletReady, onDeleted, onError }) => {
|
|
116
|
+
const startedRef = React.useRef(false)
|
|
117
|
+
React.useEffect(() => {
|
|
118
|
+
if (startedRef.current) return
|
|
119
|
+
startedRef.current = true
|
|
120
|
+
sendBrowserWalletTransaction({
|
|
121
|
+
chainId: mainnet.id,
|
|
122
|
+
expectedAccount: ownerAddress,
|
|
123
|
+
to: plan.transaction.to,
|
|
124
|
+
data: plan.transaction.data,
|
|
125
|
+
purpose: 'delete-ens-subdomain',
|
|
126
|
+
onReady: ready => onWalletReady(ready),
|
|
127
|
+
})
|
|
128
|
+
.then(async result => {
|
|
129
|
+
onWalletReady(null)
|
|
130
|
+
const client = createMainnetClient()
|
|
131
|
+
await client.waitForTransactionReceipt({ hash: result.txHash })
|
|
132
|
+
onDeleted()
|
|
133
|
+
})
|
|
134
|
+
.catch((err: unknown) => {
|
|
135
|
+
onWalletReady(null)
|
|
136
|
+
onError(err instanceof Error ? err.message : String(err))
|
|
137
|
+
})
|
|
138
|
+
}, [])
|
|
139
|
+
return (
|
|
140
|
+
<WalletApprovalScreen
|
|
141
|
+
title="Delete ENS Subdomain"
|
|
142
|
+
subtitle={`Clearing the subnode for ${plan.fullName} at ${plan.parentName} on Ethereum mainnet.`}
|
|
143
|
+
walletSession={walletSession}
|
|
144
|
+
label="waiting for owner wallet transaction..."
|
|
145
|
+
onCancel={() => onError('Subdomain deletion cancelled.')}
|
|
146
|
+
/>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const RegisterRootTxRunner: React.FC<{
|
|
151
|
+
phase: RegisterCommitPhase
|
|
152
|
+
ownerAddress: Address
|
|
153
|
+
walletSession: BrowserWalletReady | null
|
|
154
|
+
onWalletReady: (session: BrowserWalletReady | null) => void
|
|
155
|
+
onRegistered: () => void
|
|
156
|
+
onError: (msg: string) => void
|
|
157
|
+
}> = ({ phase, ownerAddress, walletSession, onWalletReady, onRegistered, onError }) => {
|
|
158
|
+
const startedRef = React.useRef(false)
|
|
159
|
+
React.useEffect(() => {
|
|
160
|
+
if (startedRef.current) return
|
|
161
|
+
startedRef.current = true
|
|
162
|
+
const tx = encodeRegisterTransaction({
|
|
163
|
+
label: phase.label,
|
|
164
|
+
owner: ownerAddress,
|
|
165
|
+
durationSeconds: BigInt(ONE_YEAR_SECONDS),
|
|
166
|
+
secret: phase.secret,
|
|
167
|
+
rentPrice: phase.price,
|
|
168
|
+
})
|
|
169
|
+
sendBrowserWalletTransaction({
|
|
170
|
+
chainId: mainnet.id,
|
|
171
|
+
expectedAccount: ownerAddress,
|
|
172
|
+
to: tx.to,
|
|
173
|
+
data: tx.data,
|
|
174
|
+
value: tx.value,
|
|
175
|
+
purpose: 'register-root-tx',
|
|
176
|
+
onReady: ready => onWalletReady(ready),
|
|
177
|
+
})
|
|
178
|
+
.then(async result => {
|
|
179
|
+
onWalletReady(null)
|
|
180
|
+
const client = createMainnetClient()
|
|
181
|
+
await client.waitForTransactionReceipt({ hash: result.txHash })
|
|
182
|
+
onRegistered()
|
|
183
|
+
})
|
|
184
|
+
.catch((err: unknown) => {
|
|
185
|
+
onWalletReady(null)
|
|
186
|
+
onError(err instanceof Error ? err.message : String(err))
|
|
187
|
+
})
|
|
188
|
+
}, [])
|
|
189
|
+
return (
|
|
190
|
+
<WalletApprovalScreen
|
|
191
|
+
title="Register ENS Name"
|
|
192
|
+
subtitle={`Paying 1 year of rent and registering ${phase.label}.eth on Ethereum mainnet.`}
|
|
193
|
+
walletSession={walletSession}
|
|
194
|
+
label="waiting for connected wallet transaction..."
|
|
195
|
+
onCancel={() => onError('Registration cancelled. The commit will expire in 24 hours; restart from the name input to retry.')}
|
|
196
|
+
/>
|
|
197
|
+
)
|
|
198
|
+
}
|