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,236 @@
|
|
|
1
|
+
import type { Address } from 'viem'
|
|
2
|
+
import { createErc8004PublicClient } from '../../../registry/erc8004.js'
|
|
3
|
+
import { discoverPriorVaultFromTokenOwner, isAgentInVault } from '../../../registry/operatorVault.js'
|
|
4
|
+
import type { ProfileUpdates, Step } from '../../identityHubReducer.js'
|
|
5
|
+
import { isCustodyEditStep } from './CustodyEditFlow.js'
|
|
6
|
+
import { resolveOperatorVaultAddress } from './custodyEffects.js'
|
|
7
|
+
import type { CustodyFlowDeps } from './custodyFlowTypes.js'
|
|
8
|
+
import { humanOwnerAddress } from './custodyFlowHelpers.js'
|
|
9
|
+
|
|
10
|
+
export function createCustodyFlowActions({
|
|
11
|
+
config,
|
|
12
|
+
setStep,
|
|
13
|
+
handleStepError,
|
|
14
|
+
guardOwnership,
|
|
15
|
+
triggerRebackup,
|
|
16
|
+
}: CustodyFlowDeps): {
|
|
17
|
+
beginVaultDeposit: (currentStep: Step, returnTo: Step, profileUpdates: ProfileUpdates) => void
|
|
18
|
+
beginVaultUnwrap: (currentStep: Step, returnTo: Step, profileUpdates: ProfileUpdates) => void
|
|
19
|
+
beginWithdrawToken: (currentStep: Step, returnTo: Step) => void
|
|
20
|
+
beginReturnToVault: (currentStep: Step, returnTo: Step, vaultAddress: Address) => void
|
|
21
|
+
} {
|
|
22
|
+
const beginVaultDeposit = (currentStep: Step, returnTo: Step, profileUpdates: ProfileUpdates): void => {
|
|
23
|
+
if (!isCustodyEditStep(currentStep) || currentStep.kind !== 'custody-advanced-confirm') return
|
|
24
|
+
const vaultAddress = resolveOperatorVaultAddress(currentStep.identity, config?.erc8004?.operatorVaults)
|
|
25
|
+
const expectedOwnerForDiscovery = humanOwnerAddress(currentStep.identity)
|
|
26
|
+
if (!vaultAddress) {
|
|
27
|
+
const registry = currentStep.registry
|
|
28
|
+
;(async () => {
|
|
29
|
+
try {
|
|
30
|
+
const client = createErc8004PublicClient(registry)
|
|
31
|
+
const probe = await discoverPriorVaultFromTokenOwner({
|
|
32
|
+
client,
|
|
33
|
+
registry: registry.identityRegistryAddress,
|
|
34
|
+
agentId: BigInt(currentStep.identity.agentId ?? '0'),
|
|
35
|
+
expectedOwner: expectedOwnerForDiscovery,
|
|
36
|
+
})
|
|
37
|
+
if (probe.found) {
|
|
38
|
+
const recoveredVault = probe.vaultAddress
|
|
39
|
+
const recoveredProfileUpdates = withVaultProfileUpdates(profileUpdates, recoveredVault)
|
|
40
|
+
const status = await isAgentInVault({
|
|
41
|
+
client,
|
|
42
|
+
vaultAddress: recoveredVault,
|
|
43
|
+
registry: registry.identityRegistryAddress,
|
|
44
|
+
agentId: BigInt(currentStep.identity.agentId ?? '0'),
|
|
45
|
+
})
|
|
46
|
+
if (status.inVault && status.ownerAddress?.toLowerCase() === expectedOwnerForDiscovery.toLowerCase()) {
|
|
47
|
+
triggerRebackup(returnTo, recoveredProfileUpdates, { vaultAddress: recoveredVault })
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
if (status.inVault) {
|
|
51
|
+
handleStepError(
|
|
52
|
+
new Error(`Recovered operator delegation vault ${recoveredVault} holds the token, but the vault-level depositor is ${status.ownerAddress ?? 'unknown'}, not your wallet ${expectedOwnerForDiscovery}. Mid-flow recovery requires the original depositor's wallet to call vault.unwrap.`),
|
|
53
|
+
{ kind: 'custody-model', identity: currentStep.identity, registry, returnTo },
|
|
54
|
+
)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
setStep({
|
|
58
|
+
kind: 'custody-vault-deposit-tx',
|
|
59
|
+
identity: currentStep.identity,
|
|
60
|
+
registry,
|
|
61
|
+
vaultAddress: recoveredVault,
|
|
62
|
+
profileUpdates: recoveredProfileUpdates,
|
|
63
|
+
returnTo,
|
|
64
|
+
})
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
void 0
|
|
69
|
+
}
|
|
70
|
+
setStep({
|
|
71
|
+
kind: 'custody-vault-deploy-tx',
|
|
72
|
+
identity: currentStep.identity,
|
|
73
|
+
registry,
|
|
74
|
+
profileUpdates,
|
|
75
|
+
returnTo,
|
|
76
|
+
})
|
|
77
|
+
})()
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
const expectedOwner = humanOwnerAddress(currentStep.identity)
|
|
81
|
+
const registry = currentStep.registry
|
|
82
|
+
;(async () => {
|
|
83
|
+
try {
|
|
84
|
+
const client = createErc8004PublicClient(registry)
|
|
85
|
+
const vaultProfileUpdates = withVaultProfileUpdates(profileUpdates, vaultAddress)
|
|
86
|
+
const status = await isAgentInVault({
|
|
87
|
+
client,
|
|
88
|
+
vaultAddress,
|
|
89
|
+
registry: registry.identityRegistryAddress,
|
|
90
|
+
agentId: BigInt(currentStep.identity.agentId ?? '0'),
|
|
91
|
+
})
|
|
92
|
+
if (status.inVault && status.ownerAddress?.toLowerCase() === expectedOwner.toLowerCase()) {
|
|
93
|
+
triggerRebackup(returnTo, vaultProfileUpdates, { vaultAddress })
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
if (status.inVault) {
|
|
97
|
+
handleStepError(
|
|
98
|
+
new Error(`Token is held by the operator delegation vault, but the vault-level owner is ${status.ownerAddress ?? 'unknown'}, not your wallet ${expectedOwner}. Recovery requires that wallet to call vault.unwrap.`),
|
|
99
|
+
{ kind: 'custody-model', identity: currentStep.identity, registry, returnTo },
|
|
100
|
+
)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
setStep({
|
|
104
|
+
kind: 'custody-vault-deposit-tx',
|
|
105
|
+
identity: currentStep.identity,
|
|
106
|
+
registry,
|
|
107
|
+
vaultAddress,
|
|
108
|
+
profileUpdates: vaultProfileUpdates,
|
|
109
|
+
returnTo,
|
|
110
|
+
})
|
|
111
|
+
} catch (err: unknown) {
|
|
112
|
+
setStep({
|
|
113
|
+
kind: 'custody-vault-deposit-tx',
|
|
114
|
+
identity: currentStep.identity,
|
|
115
|
+
registry,
|
|
116
|
+
vaultAddress,
|
|
117
|
+
profileUpdates: withVaultProfileUpdates(profileUpdates, vaultAddress),
|
|
118
|
+
returnTo,
|
|
119
|
+
})
|
|
120
|
+
void err
|
|
121
|
+
}
|
|
122
|
+
})()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const beginWithdrawToken = (currentStep: Step, returnTo: Step): void => {
|
|
126
|
+
if (!isCustodyEditStep(currentStep)) return
|
|
127
|
+
const vaultAddress = resolveOperatorVaultAddress(currentStep.identity, config?.erc8004?.operatorVaults)
|
|
128
|
+
if (!vaultAddress) {
|
|
129
|
+
handleStepError(
|
|
130
|
+
new Error('No agent vault is recorded for this identity. There is nothing to withdraw.'),
|
|
131
|
+
{ kind: 'custody-model', identity: currentStep.identity, registry: currentStep.registry, returnTo },
|
|
132
|
+
)
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
const depositor = humanOwnerAddress(currentStep.identity)
|
|
136
|
+
const activeAgentId = currentStep.identity.agentId
|
|
137
|
+
if (!activeAgentId) {
|
|
138
|
+
handleStepError(
|
|
139
|
+
new Error('This identity does not have an ERC-8004 token ID. There is nothing to withdraw.'),
|
|
140
|
+
{ kind: 'custody-model', identity: currentStep.identity, registry: currentStep.registry, returnTo },
|
|
141
|
+
)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
setStep({
|
|
145
|
+
kind: 'custody-vault-withdraw-discovering',
|
|
146
|
+
identity: currentStep.identity,
|
|
147
|
+
registry: currentStep.registry,
|
|
148
|
+
vaultAddress,
|
|
149
|
+
returnTo,
|
|
150
|
+
})
|
|
151
|
+
;(async () => {
|
|
152
|
+
const client = createErc8004PublicClient(currentStep.registry)
|
|
153
|
+
try {
|
|
154
|
+
const status = await isAgentInVault({
|
|
155
|
+
client,
|
|
156
|
+
vaultAddress,
|
|
157
|
+
registry: currentStep.registry.identityRegistryAddress,
|
|
158
|
+
agentId: BigInt(activeAgentId),
|
|
159
|
+
})
|
|
160
|
+
if (status.inVault) {
|
|
161
|
+
if (status.ownerAddress && status.ownerAddress.toLowerCase() !== depositor.toLowerCase()) {
|
|
162
|
+
handleStepError(
|
|
163
|
+
new Error(`Agent vault holds token #${activeAgentId} but recorded the depositor as ${status.ownerAddress}, not your wallet ${depositor}. Only the original depositor can withdraw.`),
|
|
164
|
+
{ kind: 'custody-model', identity: currentStep.identity, registry: currentStep.registry, returnTo },
|
|
165
|
+
)
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
setStep({
|
|
169
|
+
kind: 'custody-vault-withdraw-tx',
|
|
170
|
+
identity: currentStep.identity,
|
|
171
|
+
registry: currentStep.registry,
|
|
172
|
+
vaultAddress,
|
|
173
|
+
agentId: activeAgentId,
|
|
174
|
+
returnTo,
|
|
175
|
+
})
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
handleStepError(
|
|
179
|
+
new Error(`Token #${activeAgentId} is not currently in the agent vault. There is nothing to withdraw; the token is already with the owner wallet.`),
|
|
180
|
+
{ kind: 'custody-model', identity: currentStep.identity, registry: currentStep.registry, returnTo },
|
|
181
|
+
)
|
|
182
|
+
} catch (err: unknown) {
|
|
183
|
+
handleStepError(err, { kind: 'custody-model', identity: currentStep.identity, registry: currentStep.registry, returnTo })
|
|
184
|
+
}
|
|
185
|
+
})()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const beginVaultUnwrap = (currentStep: Step, returnTo: Step, profileUpdates: ProfileUpdates): void => {
|
|
189
|
+
if (!isCustodyEditStep(currentStep) || currentStep.kind !== 'custody-simple-confirm') return
|
|
190
|
+
const vaultAddress = resolveOperatorVaultAddress(currentStep.identity, config?.erc8004?.operatorVaults)
|
|
191
|
+
if (!vaultAddress) {
|
|
192
|
+
triggerRebackup(returnTo, profileUpdates, { useVault: false })
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
;(async () => {
|
|
196
|
+
const allowed = await guardOwnership(currentStep.identity, currentStep.registry, 'vault-level-owner', returnTo)
|
|
197
|
+
if (!allowed) return
|
|
198
|
+
const agentIds = currentStep.identity.agentId ? [currentStep.identity.agentId] : []
|
|
199
|
+
setStep({
|
|
200
|
+
kind: 'custody-vault-unwrap-tx',
|
|
201
|
+
identity: currentStep.identity,
|
|
202
|
+
registry: currentStep.registry,
|
|
203
|
+
vaultAddress,
|
|
204
|
+
profileUpdates,
|
|
205
|
+
returnTo,
|
|
206
|
+
agentIds,
|
|
207
|
+
})
|
|
208
|
+
})()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const beginReturnToVault = (currentStep: Step, returnTo: Step, vaultAddress: Address): void => {
|
|
212
|
+
if (!isCustodyEditStep(currentStep)) return
|
|
213
|
+
setStep({
|
|
214
|
+
kind: 'custody-vault-deposit-tx',
|
|
215
|
+
identity: currentStep.identity,
|
|
216
|
+
registry: currentStep.registry,
|
|
217
|
+
vaultAddress,
|
|
218
|
+
profileUpdates: withVaultProfileUpdates({}, vaultAddress),
|
|
219
|
+
returnTo,
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
beginVaultDeposit,
|
|
225
|
+
beginVaultUnwrap,
|
|
226
|
+
beginWithdrawToken,
|
|
227
|
+
beginReturnToVault,
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function withVaultProfileUpdates(profileUpdates: ProfileUpdates, vaultAddress: Address): ProfileUpdates {
|
|
232
|
+
return {
|
|
233
|
+
...profileUpdates,
|
|
234
|
+
operatorVaultAddress: vaultAddress,
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import { createErc8004PublicClient } from '../../../registry/erc8004.js'
|
|
3
|
+
import { confirmAgentInVault } from '../../../registry/operatorVault.js'
|
|
4
|
+
import { invalidateOwnershipCache } from '../../reconciliation/index.js'
|
|
5
|
+
import type { ProfileUpdates } from '../../identityHubReducer.js'
|
|
6
|
+
import type { CustodyFlowDeps } from './custodyFlowTypes.js'
|
|
7
|
+
import { humanOwnerAddress } from './custodyFlowHelpers.js'
|
|
8
|
+
import {
|
|
9
|
+
runVaultDeployTransaction,
|
|
10
|
+
runVaultDepositTransaction,
|
|
11
|
+
runVaultUnwrapTransaction,
|
|
12
|
+
runVaultWithdrawTransaction,
|
|
13
|
+
} from './custodyEffects.js'
|
|
14
|
+
|
|
15
|
+
export function useCustodyTransactionEffects({
|
|
16
|
+
step,
|
|
17
|
+
setStep,
|
|
18
|
+
callbacks,
|
|
19
|
+
handleStepError,
|
|
20
|
+
triggerRebackup,
|
|
21
|
+
refreshReconciliation,
|
|
22
|
+
}: CustodyFlowDeps): void {
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (step.kind !== 'custody-vault-deploy-tx') return
|
|
25
|
+
let cancelled = false
|
|
26
|
+
if (!step.identity.agentId) {
|
|
27
|
+
handleStepError(
|
|
28
|
+
new Error('Cannot deploy agent vault: agent token ID is missing'),
|
|
29
|
+
{ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo },
|
|
30
|
+
)
|
|
31
|
+
return () => { cancelled = true }
|
|
32
|
+
}
|
|
33
|
+
runVaultDeployTransaction({
|
|
34
|
+
registry: step.registry,
|
|
35
|
+
walletAddress: humanOwnerAddress(step.identity),
|
|
36
|
+
agentId: BigInt(step.identity.agentId),
|
|
37
|
+
callbacks,
|
|
38
|
+
flowId: 'advanced-custody',
|
|
39
|
+
})
|
|
40
|
+
.then(async ({ vaultAddress }) => {
|
|
41
|
+
if (cancelled) return
|
|
42
|
+
setStep({
|
|
43
|
+
kind: 'custody-vault-deposit-tx',
|
|
44
|
+
identity: step.identity,
|
|
45
|
+
registry: step.registry,
|
|
46
|
+
vaultAddress,
|
|
47
|
+
profileUpdates: withVaultProfileUpdates(step.profileUpdates, vaultAddress),
|
|
48
|
+
returnTo: step.returnTo,
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
.catch((err: unknown) => {
|
|
52
|
+
if (cancelled) return
|
|
53
|
+
handleStepError(err, { kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })
|
|
54
|
+
})
|
|
55
|
+
return () => { cancelled = true }
|
|
56
|
+
}, [step])
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (step.kind !== 'custody-vault-deposit-tx') return
|
|
60
|
+
let cancelled = false
|
|
61
|
+
runVaultDepositTransaction({
|
|
62
|
+
identity: step.identity,
|
|
63
|
+
registry: step.registry,
|
|
64
|
+
vaultAddress: step.vaultAddress,
|
|
65
|
+
callbacks,
|
|
66
|
+
flowId: 'advanced-custody',
|
|
67
|
+
})
|
|
68
|
+
.then(async () => {
|
|
69
|
+
if (cancelled) return
|
|
70
|
+
invalidateOwnershipCache()
|
|
71
|
+
refreshReconciliation()
|
|
72
|
+
const probeClient = createErc8004PublicClient(step.registry)
|
|
73
|
+
const expectedOwner = humanOwnerAddress(step.identity)
|
|
74
|
+
const status = await confirmAgentInVault({
|
|
75
|
+
client: probeClient,
|
|
76
|
+
vaultAddress: step.vaultAddress,
|
|
77
|
+
registry: step.registry.identityRegistryAddress,
|
|
78
|
+
agentId: BigInt(step.identity.agentId ?? '0'),
|
|
79
|
+
})
|
|
80
|
+
if (cancelled) return
|
|
81
|
+
if (status.ownerAddress.toLowerCase() !== expectedOwner.toLowerCase()) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Vault holds the token but recorded the depositor as ${status.ownerAddress} instead of ${expectedOwner}. Use Withdraw Token to recover.`,
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
triggerRebackup(step.returnTo ?? { kind: 'menu' }, step.profileUpdates, { vaultAddress: step.vaultAddress })
|
|
87
|
+
})
|
|
88
|
+
.catch((err: unknown) => {
|
|
89
|
+
if (cancelled) return
|
|
90
|
+
handleStepError(err, { kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })
|
|
91
|
+
})
|
|
92
|
+
return () => { cancelled = true }
|
|
93
|
+
}, [step])
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (step.kind !== 'custody-vault-withdraw-tx') return
|
|
97
|
+
let cancelled = false
|
|
98
|
+
runVaultWithdrawTransaction({
|
|
99
|
+
identity: step.identity,
|
|
100
|
+
registry: step.registry,
|
|
101
|
+
vaultAddress: step.vaultAddress,
|
|
102
|
+
callbacks,
|
|
103
|
+
...(step.agentId ? { agentId: BigInt(step.agentId) } : {}),
|
|
104
|
+
})
|
|
105
|
+
.then(({ recipient }) => {
|
|
106
|
+
if (cancelled) return
|
|
107
|
+
invalidateOwnershipCache()
|
|
108
|
+
refreshReconciliation()
|
|
109
|
+
setStep({
|
|
110
|
+
kind: 'custody-vault-withdraw-done',
|
|
111
|
+
identity: step.identity,
|
|
112
|
+
registry: step.registry,
|
|
113
|
+
vaultAddress: step.vaultAddress,
|
|
114
|
+
recipient,
|
|
115
|
+
returnTo: step.returnTo,
|
|
116
|
+
...(step.returnContext ? { returnContext: step.returnContext } : {}),
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
.catch((err: unknown) => {
|
|
120
|
+
if (cancelled) return
|
|
121
|
+
handleStepError(err, { kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })
|
|
122
|
+
})
|
|
123
|
+
return () => { cancelled = true }
|
|
124
|
+
}, [step])
|
|
125
|
+
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (step.kind !== 'custody-vault-unwrap-tx') return
|
|
128
|
+
let cancelled = false
|
|
129
|
+
const agentIds = step.agentIds && step.agentIds.length > 0
|
|
130
|
+
? step.agentIds
|
|
131
|
+
: (step.identity.agentId ? [step.identity.agentId] : [])
|
|
132
|
+
;(async () => {
|
|
133
|
+
try {
|
|
134
|
+
for (const agentIdStr of agentIds) {
|
|
135
|
+
if (cancelled) return
|
|
136
|
+
await runVaultUnwrapTransaction({
|
|
137
|
+
identity: step.identity,
|
|
138
|
+
registry: step.registry,
|
|
139
|
+
vaultAddress: step.vaultAddress,
|
|
140
|
+
callbacks,
|
|
141
|
+
flowId: 'simple-custody',
|
|
142
|
+
agentId: BigInt(agentIdStr),
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
if (cancelled) return
|
|
146
|
+
invalidateOwnershipCache()
|
|
147
|
+
refreshReconciliation()
|
|
148
|
+
triggerRebackup(step.returnTo ?? { kind: 'menu' }, step.profileUpdates, { useVault: false })
|
|
149
|
+
} catch (err: unknown) {
|
|
150
|
+
if (cancelled) return
|
|
151
|
+
handleStepError(err, { kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })
|
|
152
|
+
}
|
|
153
|
+
})()
|
|
154
|
+
return () => { cancelled = true }
|
|
155
|
+
}, [step])
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function withVaultProfileUpdates(profileUpdates: ProfileUpdates, vaultAddress: `0x${string}`): ProfileUpdates {
|
|
159
|
+
return {
|
|
160
|
+
...profileUpdates,
|
|
161
|
+
operatorVaultAddress: vaultAddress,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EthagentConfig, EthagentIdentity } from '../../../../storage/config.js'
|
|
2
|
+
import { buildSeedConfigForIdentity } from '../../../../storage/config.js'
|
|
3
|
+
import { readOwnerAddressField } from '../../../identityCompat.js'
|
|
4
|
+
import { supportedErc8004ChainForId, type Erc8004RegistryConfig } from '../../../registry/erc8004.js'
|
|
5
|
+
|
|
6
|
+
export function chainLabel(chainId: number): string {
|
|
7
|
+
return supportedErc8004ChainForId(chainId)?.name ?? `chain ${chainId}`
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function humanOwnerAddress(identity: EthagentIdentity): `0x${string}` {
|
|
11
|
+
const stateOwnerAddress = readOwnerAddressField(identity.state as Record<string, unknown> | undefined)
|
|
12
|
+
if (stateOwnerAddress && /^0x[a-fA-F0-9]{40}$/.test(stateOwnerAddress)) {
|
|
13
|
+
return stateOwnerAddress as `0x${string}`
|
|
14
|
+
}
|
|
15
|
+
return (identity.ownerAddress ?? identity.address) as `0x${string}`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function buildSeedConfigFromStep(identity: EthagentIdentity, registry: Erc8004RegistryConfig): EthagentConfig {
|
|
19
|
+
return buildSeedConfigForIdentity({
|
|
20
|
+
identity,
|
|
21
|
+
chainId: registry.chainId,
|
|
22
|
+
rpcUrl: registry.rpcUrl,
|
|
23
|
+
identityRegistryAddress: registry.identityRegistryAddress,
|
|
24
|
+
})
|
|
25
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { Surface } from '../../../../ui/Surface.js'
|
|
4
|
+
import { Select } from '../../../../ui/Select.js'
|
|
5
|
+
import { theme } from '../../../../ui/theme.js'
|
|
6
|
+
import { WalletApprovalScreen } from '../../components/WalletApprovalScreen.js'
|
|
7
|
+
import { shortAddress } from '../../model/format.js'
|
|
8
|
+
import type { Step } from '../../identityHubReducer.js'
|
|
9
|
+
import type { CustodyFlowDeps } from './custodyFlowTypes.js'
|
|
10
|
+
import { chainLabel, humanOwnerAddress } from './custodyFlowHelpers.js'
|
|
11
|
+
|
|
12
|
+
const Row: React.FC<{ label: string; value: string }> = ({ label, value }) => (
|
|
13
|
+
<Text>
|
|
14
|
+
<Text color={theme.dim}>{label.padEnd(18)}</Text>
|
|
15
|
+
<Text color={theme.text}>{value}</Text>
|
|
16
|
+
</Text>
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export function renderCustodyStep({
|
|
20
|
+
step,
|
|
21
|
+
setStep,
|
|
22
|
+
walletSession,
|
|
23
|
+
}: CustodyFlowDeps): React.ReactElement | null {
|
|
24
|
+
if (step.kind === 'custody-vault-deploy-tx') {
|
|
25
|
+
return (
|
|
26
|
+
<WalletApprovalScreen
|
|
27
|
+
title="Deploy Operator Delegation Vault"
|
|
28
|
+
subtitle={`Deploying a dedicated OperatorVault for this ERC-8004 token on ${chainLabel(step.registry.chainId)}.`}
|
|
29
|
+
walletSession={walletSession}
|
|
30
|
+
label="waiting for owner wallet transaction..."
|
|
31
|
+
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
if (step.kind === 'custody-vault-deposit-tx') {
|
|
36
|
+
return (
|
|
37
|
+
<WalletApprovalScreen
|
|
38
|
+
title="Deposit Token Into Operator Delegation Vault"
|
|
39
|
+
subtitle={`Sign one ${chainLabel(step.registry.chainId)} transaction. Sends ERC-8004 token #${step.identity.agentId ?? ''} to its agent vault.`}
|
|
40
|
+
walletSession={walletSession}
|
|
41
|
+
label="waiting for token-owner wallet transaction..."
|
|
42
|
+
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
43
|
+
/>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
if (step.kind === 'custody-vault-withdraw-discovering') {
|
|
47
|
+
const targetAgentId = step.identity.agentId ?? ''
|
|
48
|
+
return (
|
|
49
|
+
<Surface
|
|
50
|
+
title="Checking Vault"
|
|
51
|
+
subtitle={targetAgentId
|
|
52
|
+
? `Confirming the agent vault holds ERC-8004 token #${targetAgentId} on ${chainLabel(step.registry.chainId)}.`
|
|
53
|
+
: `Checking this identity's recorded agent vault on ${chainLabel(step.registry.chainId)}.`}
|
|
54
|
+
footer={<Text color={theme.dim}>esc cancel</Text>}
|
|
55
|
+
>
|
|
56
|
+
<Box marginTop={1}>
|
|
57
|
+
<Text color={theme.textSubtle}>Reading vault state from chain...</Text>
|
|
58
|
+
</Box>
|
|
59
|
+
</Surface>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
if (step.kind === 'custody-vault-withdraw-tx') {
|
|
63
|
+
const targetAgentId = step.agentId ?? step.identity.agentId ?? ''
|
|
64
|
+
return (
|
|
65
|
+
<WalletApprovalScreen
|
|
66
|
+
title="Withdraw Token"
|
|
67
|
+
subtitle={`Unwraps ERC-8004 token #${targetAgentId} from its agent vault to your owner wallet on ${chainLabel(step.registry.chainId)}.`}
|
|
68
|
+
walletSession={walletSession}
|
|
69
|
+
label="waiting for owner wallet transaction..."
|
|
70
|
+
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
71
|
+
/>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
if (step.kind === 'custody-vault-withdraw-pick-token') {
|
|
75
|
+
const activeId = step.identity.agentId
|
|
76
|
+
const options = step.tokens.map(t => ({
|
|
77
|
+
value: t.agentId,
|
|
78
|
+
label: `Token #${t.agentId}${activeId && t.agentId === activeId ? ' (active)' : ''}`,
|
|
79
|
+
hint: 'Withdraw this token to your owner wallet',
|
|
80
|
+
}))
|
|
81
|
+
return (
|
|
82
|
+
<Surface
|
|
83
|
+
title="Pick a Vaulted Token"
|
|
84
|
+
subtitle={`Your wallet has ${step.tokens.length} vaulted tokens on ${chainLabel(step.registry.chainId)}. Pick one to withdraw.`}
|
|
85
|
+
footer={<Text color={theme.dim}>enter select · esc back</Text>}
|
|
86
|
+
>
|
|
87
|
+
<Box marginTop={1}>
|
|
88
|
+
<Select<string>
|
|
89
|
+
options={[
|
|
90
|
+
{ value: 'header', role: 'section', label: 'Vaulted Tokens' },
|
|
91
|
+
...options,
|
|
92
|
+
{ value: 'cancel', role: 'section', label: 'Navigation' },
|
|
93
|
+
{ value: 'cancel', label: 'Back', hint: 'Return to Custody Mode', role: 'utility' },
|
|
94
|
+
]}
|
|
95
|
+
hintLayout="inline"
|
|
96
|
+
onSubmit={choice => {
|
|
97
|
+
if (choice === 'cancel') {
|
|
98
|
+
setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
setStep({
|
|
102
|
+
kind: 'custody-vault-withdraw-tx',
|
|
103
|
+
identity: step.identity,
|
|
104
|
+
registry: step.registry,
|
|
105
|
+
vaultAddress: step.vaultAddress,
|
|
106
|
+
agentId: choice,
|
|
107
|
+
returnTo: step.returnTo,
|
|
108
|
+
...(step.returnContext ? { returnContext: step.returnContext } : {}),
|
|
109
|
+
})
|
|
110
|
+
}}
|
|
111
|
+
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
112
|
+
/>
|
|
113
|
+
</Box>
|
|
114
|
+
</Surface>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
if (step.kind === 'custody-vault-withdraw-done') {
|
|
118
|
+
const onReturnToVault = () => {
|
|
119
|
+
setStep({
|
|
120
|
+
kind: 'custody-vault-deposit-tx',
|
|
121
|
+
identity: step.identity,
|
|
122
|
+
registry: step.registry,
|
|
123
|
+
vaultAddress: step.vaultAddress,
|
|
124
|
+
profileUpdates: { operatorVaultAddress: step.vaultAddress },
|
|
125
|
+
returnTo: step.returnTo,
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
const onKeepOut = () => {
|
|
129
|
+
if (step.returnContext === 'ens' && step.returnTo) {
|
|
130
|
+
setStep(step.returnTo)
|
|
131
|
+
} else {
|
|
132
|
+
setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return (
|
|
136
|
+
<Surface
|
|
137
|
+
title="Token Returned to Owner Wallet"
|
|
138
|
+
subtitle={`Token returned to ${shortAddress(step.recipient)} on ${chainLabel(step.registry.chainId)}. The agent vault can be reused for this token.`}
|
|
139
|
+
footer={<Text color={theme.dim}>enter select · esc back</Text>}
|
|
140
|
+
>
|
|
141
|
+
<Box flexDirection="column">
|
|
142
|
+
<Text color={theme.textSubtle}>
|
|
143
|
+
{step.returnContext === 'ens'
|
|
144
|
+
? 'Use the token with your owner wallet to set ENS records, then return it to the vault to resume Advanced custody.'
|
|
145
|
+
: 'Use the token with your owner wallet for whatever you need, then return it to the vault when finished.'}
|
|
146
|
+
</Text>
|
|
147
|
+
</Box>
|
|
148
|
+
<Box marginTop={1}>
|
|
149
|
+
<Select<'return-to-vault' | 'keep-out'>
|
|
150
|
+
options={[
|
|
151
|
+
{ value: 'return-to-vault', role: 'section', label: 'Resume Advanced Custody' },
|
|
152
|
+
{ value: 'return-to-vault', label: 'Return Token to Vault', hint: 'Redeposit to this agent vault. No redeploy, no operator re-add' },
|
|
153
|
+
{ value: 'keep-out', role: 'section', label: 'Later' },
|
|
154
|
+
{ value: 'keep-out', label: 'Keep Out For Now', hint: 'Token stays with the owner wallet; redeposit any time from Custody Mode', role: 'utility' },
|
|
155
|
+
]}
|
|
156
|
+
hintLayout="inline"
|
|
157
|
+
onSubmit={choice => {
|
|
158
|
+
if (choice === 'return-to-vault') onReturnToVault()
|
|
159
|
+
else onKeepOut()
|
|
160
|
+
}}
|
|
161
|
+
onCancel={onKeepOut}
|
|
162
|
+
/>
|
|
163
|
+
</Box>
|
|
164
|
+
</Surface>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
if (step.kind === 'custody-vault-unwrap-tx') {
|
|
168
|
+
return (
|
|
169
|
+
<WalletApprovalScreen
|
|
170
|
+
title="Unwrap Token From Agent Vault"
|
|
171
|
+
subtitle={`Sign one ${chainLabel(step.registry.chainId)} transaction. Calls the agent vault's unwrap function to return ERC-8004 token #${step.identity.agentId ?? ''} to the owner wallet.`}
|
|
172
|
+
walletSession={walletSession}
|
|
173
|
+
label="waiting for owner wallet transaction..."
|
|
174
|
+
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
175
|
+
/>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
if (step.kind === 'custody-advanced-done') {
|
|
179
|
+
const state = (step.identity.state ?? {}) as Record<string, unknown>
|
|
180
|
+
const ownerWallet = humanOwnerAddress(step.identity) as string
|
|
181
|
+
const operatorCount = Array.isArray(state.approvedOperatorWallets) ? state.approvedOperatorWallets.length : 0
|
|
182
|
+
return (
|
|
183
|
+
<Surface
|
|
184
|
+
title="Advanced Custody Active"
|
|
185
|
+
subtitle="Your token is held in its own OperatorVault. Authorized operator wallets can rotate the agent URI onchain without owner signatures."
|
|
186
|
+
footer={<Text color={theme.dim}>enter continues</Text>}
|
|
187
|
+
>
|
|
188
|
+
<Box flexDirection="column">
|
|
189
|
+
{step.vaultAddress ? <Row label="Agent Vault" value={shortAddress(step.vaultAddress)} /> : null}
|
|
190
|
+
<Row label="Owner Wallet" value={shortAddress(ownerWallet)} />
|
|
191
|
+
<Row label="Operator Wallets" value={operatorCount === 1 ? '1 approved' : `${operatorCount} approved`} />
|
|
192
|
+
<Box marginTop={1}>
|
|
193
|
+
<Text color={theme.textSubtle}>Use Manage Operators to add or revoke operator wallets. Use Withdraw Token to pull this token out of its vault temporarily without dismantling advanced custody.</Text>
|
|
194
|
+
</Box>
|
|
195
|
+
</Box>
|
|
196
|
+
<Box marginTop={1}>
|
|
197
|
+
<Select<'continue'>
|
|
198
|
+
options={[{ value: 'continue', label: 'Return to Custody Mode' }]}
|
|
199
|
+
onSubmit={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
200
|
+
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
201
|
+
/>
|
|
202
|
+
</Box>
|
|
203
|
+
</Surface>
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
if (step.kind === 'custody-simple-done') {
|
|
207
|
+
const ownerWallet = humanOwnerAddress(step.identity) as string
|
|
208
|
+
return (
|
|
209
|
+
<Surface
|
|
210
|
+
title="Simple Custody Active"
|
|
211
|
+
subtitle={`The token is back with the owner wallet. Operator slots are cleared.`}
|
|
212
|
+
footer={<Text color={theme.dim}>enter continues</Text>}
|
|
213
|
+
>
|
|
214
|
+
<Box flexDirection="column">
|
|
215
|
+
<Row label="Owner Wallet" value={shortAddress(ownerWallet)} />
|
|
216
|
+
<Box marginTop={1}>
|
|
217
|
+
<Text color={theme.textSubtle}>Future URI rotations require an owner wallet signature per edit. Switch back to Advanced from Custody Mode at any time.</Text>
|
|
218
|
+
</Box>
|
|
219
|
+
</Box>
|
|
220
|
+
<Box marginTop={1}>
|
|
221
|
+
<Select<'continue'>
|
|
222
|
+
options={[{ value: 'continue', label: 'Return to Custody Mode' }]}
|
|
223
|
+
onSubmit={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
224
|
+
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
225
|
+
/>
|
|
226
|
+
</Box>
|
|
227
|
+
</Surface>
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
return null
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function renderRebackupSubtitle(
|
|
234
|
+
defaultSubtitle: React.ReactNode,
|
|
235
|
+
vaultRouted: boolean,
|
|
236
|
+
): React.ReactNode {
|
|
237
|
+
if (!vaultRouted) return defaultSubtitle
|
|
238
|
+
return <Text color={theme.textSubtle}>{defaultSubtitle} Routed through this token's agent vault.</Text>
|
|
239
|
+
}
|