ethagent 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -9
- package/package.json +2 -2
- package/src/identity/hub/components/IdentitySummary.tsx +1 -1
- package/src/identity/hub/components/MenuScreen.tsx +1 -1
- package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +4 -4
- package/src/identity/hub/effects/rebackup/operatorVault.ts +3 -3
- package/src/identity/hub/effects/vault/preflight.ts +2 -2
- package/src/identity/hub/flows/create/CreateFlow.tsx +2 -2
- package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +8 -8
- package/src/identity/hub/flows/custody/custodyEffects.ts +9 -9
- package/src/identity/hub/flows/custody/custodyFlowActions.ts +5 -5
- package/src/identity/hub/flows/custody/custodyFlowEffects.ts +1 -1
- package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +12 -12
- package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +2 -2
- package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +2 -2
- package/src/identity/hub/model/copy.ts +1 -1
- package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +1 -1
- package/src/identity/registry/erc8004/ownership.ts +2 -2
- package/src/identity/registry/operatorVault/bytecode.ts +1 -1
- package/src/identity/registry/operatorVault/read.ts +2 -2
- package/src/identity/wallet/page/copy.ts +11 -11
- package/src/storage/config.ts +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
<img src="https://raw.githubusercontent.com/baairon/ethagent/master/preview/image.png" alt="ethagent" />
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/ethagent)
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://www.npmjs.com/package/ethagent)
|
|
1
|
+
<img src="https://raw.githubusercontent.com/baairon/ethagent/refs/heads/master/preview/image.png" alt="ethagent" />
|
|
6
2
|
|
|
7
3
|
A privacy-first AI agent with a portable Ethereum identity.
|
|
8
4
|
|
|
@@ -19,7 +15,7 @@ ethagent binds an AI agent to a wallet-owned ERC-8004 token. Soul and memory sta
|
|
|
19
15
|
| --- | --- |
|
|
20
16
|
| Owner Wallet | Holds and controls the ERC-8004 agent token. Signs custody changes and, in Simple custody, every URI rotation. |
|
|
21
17
|
| Operator Wallet | Additional wallet authorized to rotate the onchain URI on behalf of the owner. Used in Advanced custody. Never receives token approval. |
|
|
22
|
-
|
|
|
18
|
+
| OperatorVault | Immutable per-agent custody contract used in Advanced custody. Holds at most one ERC-8004 token. |
|
|
23
19
|
| Snapshot | Encrypted bundle of SOUL.md, MEMORY.md, and session state. Pinned to IPFS; decrypts only against the owner wallet's signature. |
|
|
24
20
|
| Agent URI | IPFS URI stored in the ERC-8004 `tokenURI`. Resolves to the agent's published metadata. |
|
|
25
21
|
| Agent Card | Public JSON describing the agent: name, description, capabilities, and skills. Other agents fetch it for discovery. |
|
|
@@ -54,7 +50,7 @@ The Identity Hub manages everything portable about the agent:
|
|
|
54
50
|
|
|
55
51
|
- **Public Profile** edits name, description, icon, and the Agent Card.
|
|
56
52
|
- **ENS Name** links the agent to a subdomain and authorizes operator wallets to write the subdomain's records.
|
|
57
|
-
- **Custody Mode** switches between Simple and Advanced by depositing the token into its
|
|
53
|
+
- **Custody Mode** switches between Simple and Advanced by depositing the token into its OperatorVault or unwrapping it back out.
|
|
58
54
|
- **Prepare Transfer** stages a dual-wallet snapshot before sending the token externally.
|
|
59
55
|
- **Refetch Latest** pulls the most recent published snapshot back to local files.
|
|
60
56
|
- **Load Agent** accepts either an ENS name or a bare token ID, and loads any agent owned by or linked to the connected wallet.
|
|
@@ -85,7 +81,7 @@ Custody comes in two modes. Switch between them anytime from **Custody Mode**.
|
|
|
85
81
|
|
|
86
82
|
**Advanced** splits an owner wallet from one or more operator wallets. The **owner wallet** owns this agent's dedicated OperatorVault; one or more **operator wallets** handle routine URI rotations through that vault. Use Advanced when routine saves should not require an owner signature.
|
|
87
83
|
|
|
88
|
-
Granting an operator wallet ERC-721 approval would let it rotate the URI, but that same approval also lets it transfer the token away. The
|
|
84
|
+
Granting an operator wallet ERC-721 approval would let it rotate the URI, but that same approval also lets it transfer the token away. The OperatorVault holds the token instead and exposes only a URI-rotation lane for that agent. Operators never receive token approval or transfer rights, cannot touch ENS, and cannot grant rights to other operators. The owner still signs to authorize or revoke operators for the agent, withdraw the token, or transfer the agent.
|
|
89
85
|
|
|
90
86
|
The vault is an immutable Foundry contract at `contracts/src/OperatorVault.sol`. New vault deployments are dedicated per agent token and reject any other token.
|
|
91
87
|
|
|
@@ -99,7 +95,7 @@ Save the token ID + network somewhere safe. ENS records can be cleared and rebui
|
|
|
99
95
|
|
|
100
96
|
## Token Transfers
|
|
101
97
|
|
|
102
|
-
**Prepare Token Transfer** runs before any ERC-8004 token transfer, and only when the token sits directly in your wallet. An agent in Advanced custody has to switch to Simple first from Custody Mode, which unwraps the token from its
|
|
98
|
+
**Prepare Token Transfer** runs before any ERC-8004 token transfer, and only when the token sits directly in your wallet. An agent in Advanced custody has to switch to Simple first from Custody Mode, which unwraps the token from its OperatorVault back to the owner wallet.
|
|
103
99
|
|
|
104
100
|
- sender signs snapshot access, receiver signs restore access.
|
|
105
101
|
- Sender publishes the snapshot pointer to the agent URI.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ethagent",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "A privacy-first AI agent with a portable Ethereum identity",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/ethagent.js",
|
|
@@ -62,4 +62,4 @@
|
|
|
62
62
|
"@types/react": "^19.2.14",
|
|
63
63
|
"typescript": "^5.9.3"
|
|
64
64
|
}
|
|
65
|
-
}
|
|
65
|
+
}
|
|
@@ -88,7 +88,7 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
|
|
|
88
88
|
if (!vaultAddress) return null
|
|
89
89
|
return (
|
|
90
90
|
<Text>
|
|
91
|
-
<Text color={theme.dim}>{'
|
|
91
|
+
<Text color={theme.dim}>{'OperatorVault'.padEnd(12)}</Text>
|
|
92
92
|
<Text color={theme.text}>{shortAddress(vaultAddress)}</Text>
|
|
93
93
|
</Text>
|
|
94
94
|
)
|
|
@@ -91,7 +91,7 @@ export const MenuScreen: React.FC<MenuScreenProps> = ({
|
|
|
91
91
|
: null)
|
|
92
92
|
|
|
93
93
|
const walletSetupBaseHint = custodyMode === 'advanced'
|
|
94
|
-
? 'Advanced. Owner wallet,
|
|
94
|
+
? 'Advanced. Owner wallet, OperatorVault, authorized operator wallets'
|
|
95
95
|
: 'Simple. Switch to Advanced to delegate URI rotation through a dedicated OperatorVault'
|
|
96
96
|
|
|
97
97
|
const walletSetupLabel = flags?.custodyAsterisk ? 'Custody Mode *' : 'Custody Mode'
|
|
@@ -435,7 +435,7 @@ async function runOperatorWalletVaultPublicProfileSave(args: {
|
|
|
435
435
|
|
|
436
436
|
await callbacks.onIdentityComplete(
|
|
437
437
|
nextIdentity,
|
|
438
|
-
'Profile updated. ERC-8004 metadata published through the
|
|
438
|
+
'Profile updated. ERC-8004 metadata published through the OperatorVault.',
|
|
439
439
|
'update',
|
|
440
440
|
)
|
|
441
441
|
} finally {
|
|
@@ -616,10 +616,10 @@ async function assertVaultSignerCanRotateAgentUri(args: {
|
|
|
616
616
|
args: [registryAddress, args.agentId],
|
|
617
617
|
}) as Address)
|
|
618
618
|
} catch (err: unknown) {
|
|
619
|
-
throw new Error(`Could not verify
|
|
619
|
+
throw new Error(`Could not verify OperatorVault custody for agent #${args.agentId.toString()}: ${err instanceof Error ? err.message : String(err)}`)
|
|
620
620
|
}
|
|
621
621
|
if (vaultOwner === '0x0000000000000000000000000000000000000000') {
|
|
622
|
-
throw new Error(`
|
|
622
|
+
throw new Error(`OperatorVault ${vaultAddress} does not currently hold agent token #${args.agentId.toString()}. Connect the owner wallet and run "Fix Records" or return the token to the vault before retrying.`)
|
|
623
623
|
}
|
|
624
624
|
if (vaultOwner.toLowerCase() === signer.toLowerCase()) return
|
|
625
625
|
|
|
@@ -632,7 +632,7 @@ async function assertVaultSignerCanRotateAgentUri(args: {
|
|
|
632
632
|
if (isOperator) return
|
|
633
633
|
|
|
634
634
|
throw new Error(
|
|
635
|
-
`Operator wallet ${signer} is not yet authorized on the
|
|
635
|
+
`Operator wallet ${signer} is not yet authorized on the OperatorVault to rotate this agent's URI. Connect the owner wallet and run "Fix Records" or re-add this operator to grant the permission.`,
|
|
636
636
|
)
|
|
637
637
|
}
|
|
638
638
|
|
|
@@ -220,7 +220,7 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
220
220
|
}) as boolean
|
|
221
221
|
if (!isOperator) {
|
|
222
222
|
throw new Error(
|
|
223
|
-
`Operator wallet ${expectedSigner} is not yet authorized on the
|
|
223
|
+
`Operator wallet ${expectedSigner} is not yet authorized on the OperatorVault to rotate this agent's URI. Connect the owner wallet and run "Fix Records" or re-add this operator to grant the permission.`,
|
|
224
224
|
)
|
|
225
225
|
}
|
|
226
226
|
|
|
@@ -344,8 +344,8 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
const completionMessage = nextEnsName !== undefined && nextEnsName !== ((step.identity.state as Record<string, unknown> | undefined)?.ensName as string | undefined)
|
|
347
|
-
? 'Snapshot published onchain through the
|
|
348
|
-
: 'Snapshot published onchain through the
|
|
347
|
+
? 'Snapshot published onchain through the OperatorVault. ENS records remain owner-signed, switch to the owner wallet to update them.'
|
|
348
|
+
: 'Snapshot published onchain through the OperatorVault.'
|
|
349
349
|
|
|
350
350
|
return {
|
|
351
351
|
to: vaultCall.to,
|
|
@@ -7,14 +7,14 @@ import { readCustodyMode } from '../../model/custody.js'
|
|
|
7
7
|
|
|
8
8
|
export class OperatorVaultUnavailableError extends Error {
|
|
9
9
|
constructor(chainId: number) {
|
|
10
|
-
super(`
|
|
10
|
+
super(`OperatorVault is not deployed for chainId ${chainId}. Switching custody mode is unavailable until a deployment is recorded.`)
|
|
11
11
|
this.name = 'OperatorVaultUnavailableError'
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export class TokenInVaultError extends Error {
|
|
16
16
|
constructor(public vaultAddress: Address) {
|
|
17
|
-
super('Token is in the
|
|
17
|
+
super('Token is in the OperatorVault. Withdraw it first to prepare a transfer.')
|
|
18
18
|
this.name = 'TokenInVaultError'
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -128,7 +128,7 @@ export const CreateFlow: React.FC<CreateFlowProps> = ({
|
|
|
128
128
|
{ value: 'simple', role: 'section', label: 'Simple (Recommended)' },
|
|
129
129
|
{ value: 'simple', label: 'Simple', hint: 'One wallet owns the token, signs every save, and rotates the URI directly' },
|
|
130
130
|
{ value: 'advanced', role: 'section', label: 'Advanced' },
|
|
131
|
-
{ value: 'advanced', label: 'Advanced', hint: '
|
|
131
|
+
{ value: 'advanced', label: 'Advanced', hint: 'OperatorVault holds the token; owner wallet controls vault, operator wallets get URI-rotation permission' },
|
|
132
132
|
]}
|
|
133
133
|
hintLayout="inline"
|
|
134
134
|
onSubmit={onCustodySubmit}
|
|
@@ -182,7 +182,7 @@ export const CreateFlow: React.FC<CreateFlowProps> = ({
|
|
|
182
182
|
title={isAdvanced ? 'Connect Owner Wallet' : 'Sign in Wallet'}
|
|
183
183
|
subtitle={
|
|
184
184
|
isAdvanced
|
|
185
|
-
? 'This wallet will own the agent token and control the
|
|
185
|
+
? 'This wallet will own the agent token and control the OperatorVault. Operator wallets are configured after minting.'
|
|
186
186
|
: 'One browser flow signs, saves the IPFS backup, and submits the token transaction.'
|
|
187
187
|
}
|
|
188
188
|
walletSession={walletSession}
|
|
@@ -95,7 +95,7 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
95
95
|
const isAdvanced = onChainCustody === 'advanced' || midFlow || custodyMode === 'advanced'
|
|
96
96
|
const vaultHolds = onChainCustody === 'advanced' || midFlow
|
|
97
97
|
const subtitle = midFlow
|
|
98
|
-
? 'Advanced setup pending. This
|
|
98
|
+
? 'Advanced setup pending. This OperatorVault holds your token. Finish by publishing the first onchain update.'
|
|
99
99
|
: isAdvanced
|
|
100
100
|
? 'Advanced is active. Authorized operator wallets publish updates for this agent without an owner signature each time.'
|
|
101
101
|
: 'Simple is active. One wallet owns the token and signs every update.'
|
|
@@ -106,7 +106,7 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
106
106
|
options.push({
|
|
107
107
|
value: 'resume-advanced',
|
|
108
108
|
label: 'Resume Advanced Setup',
|
|
109
|
-
hint: 'Sign once to publish onchain and finish the
|
|
109
|
+
hint: 'Sign once to publish onchain and finish the OperatorVault switch.',
|
|
110
110
|
})
|
|
111
111
|
options.push({
|
|
112
112
|
value: 'cancel-advanced',
|
|
@@ -139,7 +139,7 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
139
139
|
options.push({
|
|
140
140
|
value: 'return-to-vault',
|
|
141
141
|
label: 'Return Token to Vault',
|
|
142
|
-
hint: 'Redeposit this token to its
|
|
142
|
+
hint: 'Redeposit this token to its OperatorVault. No redeploy, no operator re-add.',
|
|
143
143
|
})
|
|
144
144
|
}
|
|
145
145
|
options.push({ value: 'manage-operator-wallets', role: 'section', label: 'Operators' })
|
|
@@ -184,7 +184,7 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
184
184
|
})()}
|
|
185
185
|
<Row label="Custody" value={modeLabel} />
|
|
186
186
|
<Row label="Owner" value={shortAddress(ownerAddress || tokenOwner)} />
|
|
187
|
-
{isAdvanced && vaultAddress ? <Row label="
|
|
187
|
+
{isAdvanced && vaultAddress ? <Row label="OperatorVault" value={shortAddress(vaultAddress)} /> : null}
|
|
188
188
|
{isAdvanced ? (
|
|
189
189
|
<Row
|
|
190
190
|
label="Operators"
|
|
@@ -271,7 +271,7 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
271
271
|
<Select<Action>
|
|
272
272
|
options={[
|
|
273
273
|
{ value: 'confirm', role: 'section', label: 'Confirm' },
|
|
274
|
-
{ value: 'confirm', label: 'Yes, Switch to Advanced', hint: `Sign with ${shortAddress(ownerAddress || tokenOwner)} to deposit this token into its
|
|
274
|
+
{ value: 'confirm', label: 'Yes, Switch to Advanced', hint: `Sign with ${shortAddress(ownerAddress || tokenOwner)} to deposit this token into its OperatorVault` },
|
|
275
275
|
{ value: 'transfer', role: 'section', label: 'Move Token First' },
|
|
276
276
|
{ value: 'transfer', label: 'Prepare Token Transfer', hint: 'Move the token to a different wallet first, with snapshot handoff' },
|
|
277
277
|
{ value: 'back', role: 'section', label: 'Cancel' },
|
|
@@ -300,7 +300,7 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
300
300
|
return (
|
|
301
301
|
<Surface
|
|
302
302
|
title="Switch to Simple"
|
|
303
|
-
subtitle="Unwraps this ERC-8004 token from its
|
|
303
|
+
subtitle="Unwraps this ERC-8004 token from its OperatorVault and returns it directly to the owner wallet."
|
|
304
304
|
footer={footerHint('enter confirm · esc back')}
|
|
305
305
|
>
|
|
306
306
|
<Box flexDirection="column">
|
|
@@ -309,13 +309,13 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
309
309
|
<Text> </Text>
|
|
310
310
|
<Text color={theme.accentBlue}>Operators lose decrypt access on future snapshots immediately.</Text>
|
|
311
311
|
<Text color={theme.textSubtle}>Operator approvals are cleared from local state for future snapshots. Revoke onchain via Manage Operators first if needed.</Text>
|
|
312
|
-
<Text color={theme.textSubtle}>This switch calls the
|
|
312
|
+
<Text color={theme.textSubtle}>This switch calls the OperatorVault unwrap function for this token, so the owner wallet must sign the transaction.</Text>
|
|
313
313
|
</Box>
|
|
314
314
|
<Box marginTop={1}>
|
|
315
315
|
<Select<Action>
|
|
316
316
|
options={[
|
|
317
317
|
{ value: 'confirm', role: 'section', label: 'Confirm' },
|
|
318
|
-
{ value: 'confirm', label: 'Yes, Switch to Simple', hint: `Sign with the owner wallet to unwrap ${tokenLabel} from its
|
|
318
|
+
{ value: 'confirm', label: 'Yes, Switch to Simple', hint: `Sign with the owner wallet to unwrap ${tokenLabel} from its OperatorVault` },
|
|
319
319
|
{ value: 'back', role: 'section', label: 'Cancel' },
|
|
320
320
|
{ value: 'back', label: 'No, Go Back', hint: 'Return without changing custody', role: 'utility' },
|
|
321
321
|
]}
|
|
@@ -86,9 +86,9 @@ async function runVaultDeployTransactionInner(args: {
|
|
|
86
86
|
})
|
|
87
87
|
args.callbacks.onWalletReady(null)
|
|
88
88
|
const client = args.publicClient ?? createErc8004PublicClient(args.registry)
|
|
89
|
-
const receipt = await awaitConfirmedReceipt(client, result.txHash, '
|
|
89
|
+
const receipt = await awaitConfirmedReceipt(client, result.txHash, 'OperatorVault deploy', { kind: 'vault-deploy', chainId: args.registry.chainId })
|
|
90
90
|
if (!receipt.contractAddress) {
|
|
91
|
-
throw new Error('
|
|
91
|
+
throw new Error('OperatorVault deploy receipt is missing contractAddress; the transaction was not a contract creation')
|
|
92
92
|
}
|
|
93
93
|
const vaultAddress = getAddress(receipt.contractAddress)
|
|
94
94
|
await assertVaultBytecode(client, vaultAddress, result.txHash)
|
|
@@ -114,7 +114,7 @@ async function runVaultDepositTransactionInner(args: {
|
|
|
114
114
|
}): Promise<{ txHash: string }> {
|
|
115
115
|
const { identity, registry, vaultAddress } = args
|
|
116
116
|
if (!identity.agentId) {
|
|
117
|
-
throw new Error('Cannot deposit token to
|
|
117
|
+
throw new Error('Cannot deposit token to OperatorVault: agent token ID is missing')
|
|
118
118
|
}
|
|
119
119
|
const tokenOwner = getAddress(identity.ownerAddress ?? identity.address)
|
|
120
120
|
await assertVaultCanAcceptAgent({
|
|
@@ -152,7 +152,7 @@ async function runVaultDepositTransactionInner(args: {
|
|
|
152
152
|
await awaitConfirmedReceipt(
|
|
153
153
|
depositClient,
|
|
154
154
|
result.txHash as Hex,
|
|
155
|
-
'
|
|
155
|
+
'OperatorVault deposit',
|
|
156
156
|
{ kind: 'vault-deposit', chainId: registry.chainId },
|
|
157
157
|
)
|
|
158
158
|
return { txHash: result.txHash }
|
|
@@ -179,9 +179,9 @@ async function assertVaultCanAcceptAgent(args: {
|
|
|
179
179
|
const expectedRegistry = getAddress(args.registry.identityRegistryAddress)
|
|
180
180
|
const sameAgent = heldRegistry.toLowerCase() === expectedRegistry.toLowerCase() && heldAgentId === args.agentId
|
|
181
181
|
if (sameAgent) {
|
|
182
|
-
throw new Error(`
|
|
182
|
+
throw new Error(`OperatorVault ${getAddress(args.vaultAddress)} already holds ERC-8004 token #${args.agentId.toString()}. Publish the pending update instead of depositing again.`)
|
|
183
183
|
}
|
|
184
|
-
throw new Error(`
|
|
184
|
+
throw new Error(`OperatorVault ${getAddress(args.vaultAddress)} already holds ERC-8004 token #${heldAgentId.toString()} for registry ${getAddress(heldRegistry)}. Deploy a fresh vault for this agent.`)
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
export async function runVaultUnwrapTransaction(args: {
|
|
@@ -206,7 +206,7 @@ async function runVaultUnwrapTransactionInner(args: {
|
|
|
206
206
|
const { identity, registry, vaultAddress } = args
|
|
207
207
|
const targetAgentId = args.agentId ?? (identity.agentId ? BigInt(identity.agentId) : undefined)
|
|
208
208
|
if (targetAgentId === undefined) {
|
|
209
|
-
throw new Error('Cannot unwrap token from
|
|
209
|
+
throw new Error('Cannot unwrap token from OperatorVault: agent token ID is missing')
|
|
210
210
|
}
|
|
211
211
|
const baseState = (identity.state ?? {}) as Record<string, unknown>
|
|
212
212
|
const ownerAddressRaw = readOwnerAddressField(baseState)
|
|
@@ -240,7 +240,7 @@ async function runVaultUnwrapTransactionInner(args: {
|
|
|
240
240
|
await awaitConfirmedReceipt(
|
|
241
241
|
publicClient,
|
|
242
242
|
result.txHash as Hex,
|
|
243
|
-
'
|
|
243
|
+
'OperatorVault unwrap',
|
|
244
244
|
{ kind: 'vault-unwrap', chainId: registry.chainId },
|
|
245
245
|
)
|
|
246
246
|
await confirmAgentWithdrawnFromVault({
|
|
@@ -307,7 +307,7 @@ async function runVaultWithdrawTransactionInner(args: {
|
|
|
307
307
|
await awaitConfirmedReceipt(
|
|
308
308
|
publicClient,
|
|
309
309
|
result.txHash as Hex,
|
|
310
|
-
'
|
|
310
|
+
'OperatorVault withdraw',
|
|
311
311
|
{ kind: 'vault-withdraw', chainId: registry.chainId },
|
|
312
312
|
)
|
|
313
313
|
await confirmAgentWithdrawnFromVault({
|
|
@@ -49,7 +49,7 @@ export function createCustodyFlowActions({
|
|
|
49
49
|
}
|
|
50
50
|
if (status.inVault) {
|
|
51
51
|
handleStepError(
|
|
52
|
-
new Error(`Recovered
|
|
52
|
+
new Error(`Recovered OperatorVault ${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
53
|
{ kind: 'custody-model', identity: currentStep.identity, registry, returnTo },
|
|
54
54
|
)
|
|
55
55
|
return
|
|
@@ -95,7 +95,7 @@ export function createCustodyFlowActions({
|
|
|
95
95
|
}
|
|
96
96
|
if (status.inVault) {
|
|
97
97
|
handleStepError(
|
|
98
|
-
new Error(`Token is held by the
|
|
98
|
+
new Error(`Token is held by the OperatorVault, but the vault-level owner is ${status.ownerAddress ?? 'unknown'}, not your wallet ${expectedOwner}. Recovery requires that wallet to call vault.unwrap.`),
|
|
99
99
|
{ kind: 'custody-model', identity: currentStep.identity, registry, returnTo },
|
|
100
100
|
)
|
|
101
101
|
return
|
|
@@ -127,7 +127,7 @@ export function createCustodyFlowActions({
|
|
|
127
127
|
const vaultAddress = resolveOperatorVaultAddress(currentStep.identity, config?.erc8004?.operatorVaults)
|
|
128
128
|
if (!vaultAddress) {
|
|
129
129
|
handleStepError(
|
|
130
|
-
new Error('No
|
|
130
|
+
new Error('No OperatorVault is recorded for this identity. There is nothing to withdraw.'),
|
|
131
131
|
{ kind: 'custody-model', identity: currentStep.identity, registry: currentStep.registry, returnTo },
|
|
132
132
|
)
|
|
133
133
|
return
|
|
@@ -160,7 +160,7 @@ export function createCustodyFlowActions({
|
|
|
160
160
|
if (status.inVault) {
|
|
161
161
|
if (status.ownerAddress && status.ownerAddress.toLowerCase() !== depositor.toLowerCase()) {
|
|
162
162
|
handleStepError(
|
|
163
|
-
new Error(`
|
|
163
|
+
new Error(`OperatorVault holds token #${activeAgentId} but recorded the depositor as ${status.ownerAddress}, not your wallet ${depositor}. Only the original depositor can withdraw.`),
|
|
164
164
|
{ kind: 'custody-model', identity: currentStep.identity, registry: currentStep.registry, returnTo },
|
|
165
165
|
)
|
|
166
166
|
return
|
|
@@ -176,7 +176,7 @@ export function createCustodyFlowActions({
|
|
|
176
176
|
return
|
|
177
177
|
}
|
|
178
178
|
handleStepError(
|
|
179
|
-
new Error(`Token #${activeAgentId} is not currently in the
|
|
179
|
+
new Error(`Token #${activeAgentId} is not currently in the OperatorVault. There is nothing to withdraw; the token is already with the owner wallet.`),
|
|
180
180
|
{ kind: 'custody-model', identity: currentStep.identity, registry: currentStep.registry, returnTo },
|
|
181
181
|
)
|
|
182
182
|
} catch (err: unknown) {
|
|
@@ -25,7 +25,7 @@ export function useCustodyTransactionEffects({
|
|
|
25
25
|
let cancelled = false
|
|
26
26
|
if (!step.identity.agentId) {
|
|
27
27
|
handleStepError(
|
|
28
|
-
new Error('Cannot deploy
|
|
28
|
+
new Error('Cannot deploy OperatorVault: agent token ID is missing'),
|
|
29
29
|
{ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo },
|
|
30
30
|
)
|
|
31
31
|
return () => { cancelled = true }
|
|
@@ -24,7 +24,7 @@ export function renderCustodyStep({
|
|
|
24
24
|
if (step.kind === 'custody-vault-deploy-tx') {
|
|
25
25
|
return (
|
|
26
26
|
<WalletApprovalScreen
|
|
27
|
-
title="Deploy
|
|
27
|
+
title="Deploy OperatorVault"
|
|
28
28
|
subtitle={`Deploying a dedicated OperatorVault for this ERC-8004 token on ${chainLabel(step.registry.chainId)}.`}
|
|
29
29
|
walletSession={walletSession}
|
|
30
30
|
label="waiting for owner wallet transaction..."
|
|
@@ -35,8 +35,8 @@ export function renderCustodyStep({
|
|
|
35
35
|
if (step.kind === 'custody-vault-deposit-tx') {
|
|
36
36
|
return (
|
|
37
37
|
<WalletApprovalScreen
|
|
38
|
-
title="Deposit Token Into
|
|
39
|
-
subtitle={`Sign one ${chainLabel(step.registry.chainId)} transaction. Sends ERC-8004 token #${step.identity.agentId ?? ''} to its
|
|
38
|
+
title="Deposit Token Into OperatorVault"
|
|
39
|
+
subtitle={`Sign one ${chainLabel(step.registry.chainId)} transaction. Sends ERC-8004 token #${step.identity.agentId ?? ''} to its OperatorVault.`}
|
|
40
40
|
walletSession={walletSession}
|
|
41
41
|
label="waiting for token-owner wallet transaction..."
|
|
42
42
|
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
@@ -49,8 +49,8 @@ export function renderCustodyStep({
|
|
|
49
49
|
<Surface
|
|
50
50
|
title="Checking Vault"
|
|
51
51
|
subtitle={targetAgentId
|
|
52
|
-
? `Confirming the
|
|
53
|
-
: `Checking this identity's recorded
|
|
52
|
+
? `Confirming the OperatorVault holds ERC-8004 token #${targetAgentId} on ${chainLabel(step.registry.chainId)}.`
|
|
53
|
+
: `Checking this identity's recorded OperatorVault on ${chainLabel(step.registry.chainId)}.`}
|
|
54
54
|
footer={<Text color={theme.dim}>esc cancel</Text>}
|
|
55
55
|
>
|
|
56
56
|
<Box marginTop={1}>
|
|
@@ -64,7 +64,7 @@ export function renderCustodyStep({
|
|
|
64
64
|
return (
|
|
65
65
|
<WalletApprovalScreen
|
|
66
66
|
title="Withdraw Token"
|
|
67
|
-
subtitle={`Unwraps ERC-8004 token #${targetAgentId} from its
|
|
67
|
+
subtitle={`Unwraps ERC-8004 token #${targetAgentId} from its OperatorVault to your owner wallet on ${chainLabel(step.registry.chainId)}.`}
|
|
68
68
|
walletSession={walletSession}
|
|
69
69
|
label="waiting for owner wallet transaction..."
|
|
70
70
|
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
@@ -135,7 +135,7 @@ export function renderCustodyStep({
|
|
|
135
135
|
return (
|
|
136
136
|
<Surface
|
|
137
137
|
title="Token Returned to Owner Wallet"
|
|
138
|
-
subtitle={`Token returned to ${shortAddress(step.recipient)} on ${chainLabel(step.registry.chainId)}. The
|
|
138
|
+
subtitle={`Token returned to ${shortAddress(step.recipient)} on ${chainLabel(step.registry.chainId)}. The OperatorVault can be reused for this token.`}
|
|
139
139
|
footer={<Text color={theme.dim}>enter select · esc back</Text>}
|
|
140
140
|
>
|
|
141
141
|
<Box flexDirection="column">
|
|
@@ -149,7 +149,7 @@ export function renderCustodyStep({
|
|
|
149
149
|
<Select<'return-to-vault' | 'keep-out'>
|
|
150
150
|
options={[
|
|
151
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
|
|
152
|
+
{ value: 'return-to-vault', label: 'Return Token to Vault', hint: 'Redeposit to this OperatorVault. No redeploy, no operator re-add' },
|
|
153
153
|
{ value: 'keep-out', role: 'section', label: 'Later' },
|
|
154
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
155
|
]}
|
|
@@ -167,8 +167,8 @@ export function renderCustodyStep({
|
|
|
167
167
|
if (step.kind === 'custody-vault-unwrap-tx') {
|
|
168
168
|
return (
|
|
169
169
|
<WalletApprovalScreen
|
|
170
|
-
title="Unwrap Token From
|
|
171
|
-
subtitle={`Sign one ${chainLabel(step.registry.chainId)} transaction. Calls the
|
|
170
|
+
title="Unwrap Token From OperatorVault"
|
|
171
|
+
subtitle={`Sign one ${chainLabel(step.registry.chainId)} transaction. Calls the OperatorVault unwrap function to return ERC-8004 token #${step.identity.agentId ?? ''} to the owner wallet.`}
|
|
172
172
|
walletSession={walletSession}
|
|
173
173
|
label="waiting for owner wallet transaction..."
|
|
174
174
|
onCancel={() => setStep({ kind: 'custody-model', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
|
|
@@ -186,7 +186,7 @@ export function renderCustodyStep({
|
|
|
186
186
|
footer={<Text color={theme.dim}>enter continues</Text>}
|
|
187
187
|
>
|
|
188
188
|
<Box flexDirection="column">
|
|
189
|
-
{step.vaultAddress ? <Row label="
|
|
189
|
+
{step.vaultAddress ? <Row label="OperatorVault" value={shortAddress(step.vaultAddress)} /> : null}
|
|
190
190
|
<Row label="Owner Wallet" value={shortAddress(ownerWallet)} />
|
|
191
191
|
<Row label="Operator Wallets" value={operatorCount === 1 ? '1 approved' : `${operatorCount} approved`} />
|
|
192
192
|
<Box marginTop={1}>
|
|
@@ -235,5 +235,5 @@ export function renderRebackupSubtitle(
|
|
|
235
235
|
vaultRouted: boolean,
|
|
236
236
|
): React.ReactNode {
|
|
237
237
|
if (!vaultRouted) return defaultSubtitle
|
|
238
|
-
return <Text color={theme.textSubtle}>{defaultSubtitle} Routed through this token's
|
|
238
|
+
return <Text color={theme.textSubtle}>{defaultSubtitle} Routed through this token's OperatorVault.</Text>
|
|
239
239
|
}
|
|
@@ -89,7 +89,7 @@ export function renderAdvancedEnsPhase({
|
|
|
89
89
|
<Box marginTop={1} flexDirection="column">
|
|
90
90
|
<EnsSetupRow label="Owner wallet" value={`Holds ERC-8004 token #${agentId ?? 'unknown'} and signs ENS records.`} />
|
|
91
91
|
<EnsSetupRow label="Operator wallet" value="Restores snapshots; never controls the token." />
|
|
92
|
-
<EnsSetupRow label="Token moves" value="If the token is in the
|
|
92
|
+
<EnsSetupRow label="Token moves" value="If the token is in the OperatorVault, withdraw it first from Custody Mode." />
|
|
93
93
|
</Box>
|
|
94
94
|
</Box>
|
|
95
95
|
<Box marginTop={1}>
|
|
@@ -217,7 +217,7 @@ export function renderAdvancedEnsPhase({
|
|
|
217
217
|
<Box flexDirection="column">
|
|
218
218
|
<Text color={theme.dim}>Agent ENS: <Text color={theme.text}>{label}.{rootName}</Text></Text>
|
|
219
219
|
{phase.registryAction ? <Text color={theme.dim}>{advancedSubdomainStatusText(phase.registryAction)}</Text> : null}
|
|
220
|
-
<Text color={theme.dim}>Choose the operator wallet for snapshot restore access and onchain ERC-8004 URI rotation via the
|
|
220
|
+
<Text color={theme.dim}>Choose the operator wallet for snapshot restore access and onchain ERC-8004 URI rotation via the OperatorVault.</Text>
|
|
221
221
|
<Text color={theme.dim}>The operator wallet has no authority over this ENS subdomain or any token transfer; the owner wallet is the sole signer for both.</Text>
|
|
222
222
|
<Text color={theme.dim}>We only read the operator's address here so it can be added to the snapshot envelope and vault operator list later.</Text>
|
|
223
223
|
{savedOperator ? <Text color={theme.dim}>Saved operator wallet: <Text color={theme.text}>{shortAddress(savedOperator)}</Text></Text> : null}
|
|
@@ -218,9 +218,9 @@ export const EnsSetupBlockedScreen: React.FC<EnsSetupBlockedScreenProps> = ({
|
|
|
218
218
|
? (
|
|
219
219
|
<>
|
|
220
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
|
|
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 OperatorVault while the ENS subdomain stays with the owner wallet.</Text>
|
|
222
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
|
|
223
|
+
<Text color={theme.dim}>Operator wallets have no authority on this name; they only rotate the onchain ERC-8004 URI via the OperatorVault.</Text>
|
|
224
224
|
</>
|
|
225
225
|
)
|
|
226
226
|
: null}
|
|
@@ -21,7 +21,7 @@ export function copyableIdentityFields(identity?: EthagentIdentity, config?: Eth
|
|
|
21
21
|
const custodyMode = readCustodyMode(identity.state)
|
|
22
22
|
if (custodyMode === 'advanced') {
|
|
23
23
|
const vaultAddress = readIdentityStateString(identity.state, 'operatorVaultAddress')
|
|
24
|
-
if (vaultAddress) fields.push({ label: '
|
|
24
|
+
if (vaultAddress) fields.push({ label: 'OperatorVault', value: vaultAddress })
|
|
25
25
|
}
|
|
26
26
|
const activeOperator = readIdentityStateString(identity.state, 'activeOperatorAddress')
|
|
27
27
|
if (activeOperator) fields.push({ label: 'Operator Wallet', value: activeOperator })
|
|
@@ -85,7 +85,7 @@ async function runOwnershipPreflight(args: {
|
|
|
85
85
|
return {
|
|
86
86
|
ok: false,
|
|
87
87
|
reason: 'not-owned',
|
|
88
|
-
detail: `Token is held by the
|
|
88
|
+
detail: `Token is held by the OperatorVault (${directOwner.owner}). Withdraw it first.`,
|
|
89
89
|
onChainOwner: directOwner.owner,
|
|
90
90
|
}
|
|
91
91
|
}
|
|
@@ -54,7 +54,7 @@ export async function validateErc8004TokenOwner(args: Erc8004RegistryConfig & {
|
|
|
54
54
|
return {
|
|
55
55
|
ok: false,
|
|
56
56
|
reason: 'token-owner-lookup-failed',
|
|
57
|
-
detail: `ERC-8004 token #${args.agentId.toString()} is still reported at the
|
|
57
|
+
detail: `ERC-8004 token #${args.agentId.toString()} is still reported at the OperatorVault, but that vault record is empty. Ownership is still settling; retry shortly.`,
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
if (vaultOwnerResult.kind === 'error') {
|
|
@@ -73,7 +73,7 @@ export async function validateErc8004TokenOwner(args: Erc8004RegistryConfig & {
|
|
|
73
73
|
ok: false,
|
|
74
74
|
reason: 'token-owner-mismatch',
|
|
75
75
|
ownerAddress: vaultOwner,
|
|
76
|
-
detail: `ERC-8004 token #${args.agentId.toString()} is held by the
|
|
76
|
+
detail: `ERC-8004 token #${args.agentId.toString()} is held by the OperatorVault for ${vaultOwner}`,
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
return {
|
|
@@ -15,7 +15,7 @@ export class OperatorVaultBytecodeMismatchError extends Error {
|
|
|
15
15
|
txHash?: Hex,
|
|
16
16
|
) {
|
|
17
17
|
super(
|
|
18
|
-
'Deployed contract bytecode does not match the expected
|
|
18
|
+
'Deployed contract bytecode does not match the expected OperatorVault. The deploy transaction may have been intercepted.',
|
|
19
19
|
)
|
|
20
20
|
this.name = 'OperatorVaultBytecodeMismatchError'
|
|
21
21
|
this.vaultAddress = vaultAddress
|
|
@@ -97,7 +97,7 @@ export async function confirmAgentInVault(
|
|
|
97
97
|
}
|
|
98
98
|
if (lastErr) throw lastErr
|
|
99
99
|
throw new Error(
|
|
100
|
-
`
|
|
100
|
+
`OperatorVault ${getAddress(args.vaultAddress)} does not hold agent token #${args.agentId.toString()} for registry ${getAddress(args.registry)} after the deposit-confirmation budget was exhausted. The deposit transaction may have been re-orged or applied to the wrong vault. Re-run the switch.`,
|
|
101
101
|
)
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -135,7 +135,7 @@ export async function confirmAgentWithdrawnFromVault(
|
|
|
135
135
|
}
|
|
136
136
|
if (lastErr) throw lastErr
|
|
137
137
|
throw new Error(
|
|
138
|
-
`
|
|
138
|
+
`OperatorVault ${getAddress(args.vaultAddress)} did not release agent token #${args.agentId.toString()} to ${recipient} after the withdraw-confirmation budget was exhausted. Last observed: ${lastObserved ?? 'unknown'}.`,
|
|
139
139
|
)
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -93,7 +93,7 @@ export const PURPOSE_COPY: Record<string, PurposeCopyEntry> = {
|
|
|
93
93
|
flowTitle: "Operator Wallet Save",
|
|
94
94
|
sign: { text: "Sign With Operator Wallet", hint: "Signs the encrypted snapshot for restore access." },
|
|
95
95
|
prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
|
|
96
|
-
transaction: { text: "Use Operator Wallet", hint: "Rotates the ERC-8004 token URI through the
|
|
96
|
+
transaction: { text: "Use Operator Wallet", hint: "Rotates the ERC-8004 token URI through the OperatorVault." },
|
|
97
97
|
},
|
|
98
98
|
"update-snapshot-connected": {
|
|
99
99
|
flowTitle: "Save Snapshot",
|
|
@@ -196,7 +196,7 @@ export const PURPOSE_COPY: Record<string, PurposeCopyEntry> = {
|
|
|
196
196
|
"sync-operator-vault": {
|
|
197
197
|
flowTitle: "Owner Wallet Required",
|
|
198
198
|
prepare: { text: "Preparing Vault Operator Update...", hint: "Keep this page open." },
|
|
199
|
-
transaction: { text: "Use Owner Wallet", hint: "Updates the
|
|
199
|
+
transaction: { text: "Use Owner Wallet", hint: "Updates the OperatorVault metadata-operator list so authorized operator wallets can rotate the agent URI." },
|
|
200
200
|
},
|
|
201
201
|
"refetch-snapshot": {
|
|
202
202
|
flowTitle: "Refetch Latest Snapshot",
|
|
@@ -219,39 +219,39 @@ export const PURPOSE_COPY: Record<string, PurposeCopyEntry> = {
|
|
|
219
219
|
transaction: { text: "Use Sender Wallet", hint: "Submits one transaction to publish the transfer snapshot to the ERC-8004 token URI." },
|
|
220
220
|
},
|
|
221
221
|
"deploy-agent-vault": {
|
|
222
|
-
flowTitle: "Deploy
|
|
222
|
+
flowTitle: "Deploy OperatorVault",
|
|
223
223
|
prepare: { text: "Preparing Vault Deploy...", hint: "Keep this page open." },
|
|
224
|
-
transaction: { text: "Use Owner Wallet", hint: "Deploys the
|
|
225
|
-
errorContext: "While submitting the
|
|
224
|
+
transaction: { text: "Use Owner Wallet", hint: "Deploys the OperatorVault contract onchain. One-time setup per agent." },
|
|
225
|
+
errorContext: "While submitting the OperatorVault deploy",
|
|
226
226
|
},
|
|
227
227
|
"deposit-agent-vault": {
|
|
228
228
|
flowTitle: "Deposit Token Into Vault",
|
|
229
229
|
prepare: { text: "Preparing Vault Deposit...", hint: "Keep this page open." },
|
|
230
|
-
transaction: { text: "Use Owner Wallet", hint: "Sends the agent token to the
|
|
231
|
-
errorContext: "While submitting the
|
|
230
|
+
transaction: { text: "Use Owner Wallet", hint: "Sends the agent token to the OperatorVault so the vault can save updates on your behalf." },
|
|
231
|
+
errorContext: "While submitting the OperatorVault deposit",
|
|
232
232
|
},
|
|
233
233
|
"unwrap-agent-vault": {
|
|
234
234
|
flowTitle: "Unwrap Token From Vault",
|
|
235
235
|
prepare: { text: "Preparing Vault Unwrap...", hint: "Keep this page open." },
|
|
236
|
-
transaction: { text: "Use Owner Wallet", hint: "Returns the agent token from the
|
|
236
|
+
transaction: { text: "Use Owner Wallet", hint: "Returns the agent token from the OperatorVault to your owner wallet." },
|
|
237
237
|
},
|
|
238
238
|
"rotate-agent-uri-vault-owner": {
|
|
239
239
|
flowTitle: "Save Update Through Vault",
|
|
240
240
|
sign: { text: "Sign With Owner Wallet", hint: "Approves the new snapshot before saving onchain. No token approval." },
|
|
241
241
|
prepare: { text: "Preparing Update...", hint: "Keep this page open." },
|
|
242
|
-
transaction: { text: "Use Owner Wallet", hint: "Saves your update onchain through the
|
|
242
|
+
transaction: { text: "Use Owner Wallet", hint: "Saves your update onchain through the OperatorVault. The vault holds your token, so updates go through it." },
|
|
243
243
|
},
|
|
244
244
|
"rotate-agent-uri-vault-operator": {
|
|
245
245
|
flowTitle: "Save Update Through Vault",
|
|
246
246
|
sign: { text: "Sign With Operator Wallet", hint: "Approves the new snapshot before saving onchain. No token approval." },
|
|
247
247
|
prepare: { text: "Preparing Update...", hint: "Keep this page open." },
|
|
248
|
-
transaction: { text: "Use Operator Wallet", hint: "Rotates the ERC-8004 token URI through the
|
|
248
|
+
transaction: { text: "Use Operator Wallet", hint: "Rotates the ERC-8004 token URI through the OperatorVault. The vault holds your token, so the operator wallet calls the vault to publish." },
|
|
249
249
|
},
|
|
250
250
|
"withdraw-vault": {
|
|
251
251
|
flowTitle: "Withdraw Token From Vault",
|
|
252
252
|
prepare: { text: "Preparing Token Withdrawal...", hint: "Keep this page open." },
|
|
253
253
|
transaction: { text: "Use Owner Wallet", hint: "Temporarily returns the agent token from the vault to your owner wallet. Vault stays configured so you can redeposit later." },
|
|
254
|
-
errorContext: "While submitting the
|
|
254
|
+
errorContext: "While submitting the OperatorVault withdraw",
|
|
255
255
|
},
|
|
256
256
|
"register-root-commit": {
|
|
257
257
|
flowTitle: "Commit ENS Name",
|
package/src/storage/config.ts
CHANGED
|
@@ -229,7 +229,7 @@ export function setConfiguredOperatorVaultAddress(
|
|
|
229
229
|
vaultAddress: string,
|
|
230
230
|
): EthagentConfig {
|
|
231
231
|
if (!config.erc8004) {
|
|
232
|
-
throw new Error('Cannot record
|
|
232
|
+
throw new Error('Cannot record OperatorVault address: erc8004 registry config is not set')
|
|
233
233
|
}
|
|
234
234
|
return {
|
|
235
235
|
...config,
|