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
|
@@ -1,1106 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
export function chainIdForNetwork(network: SelectableNetwork): number {
|
|
55
|
-
return NETWORK_TO_CHAIN_ID[network]
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function networkForChainId(chainId: number): SelectableNetwork | undefined {
|
|
59
|
-
for (const [network, id] of Object.entries(NETWORK_TO_CHAIN_ID) as Array<[SelectableNetwork, number]>) {
|
|
60
|
-
if (id === chainId) return network
|
|
61
|
-
}
|
|
62
|
-
return undefined
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export class MissingRegistryAddressError extends Error {
|
|
66
|
-
chainId: number
|
|
67
|
-
network?: SelectableNetwork
|
|
68
|
-
constructor(chainId: number) {
|
|
69
|
-
const network = networkForChainId(chainId)
|
|
70
|
-
super(`no default ERC-8004 registry on chain ${chainId}${network ? ` (${network})` : ''}`)
|
|
71
|
-
this.name = 'MissingRegistryAddressError'
|
|
72
|
-
this.chainId = chainId
|
|
73
|
-
this.network = network
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const ERC8004_ABI = parseAbi([
|
|
78
|
-
'function register(string agentURI) returns (uint256)',
|
|
79
|
-
'function balanceOf(address owner) view returns (uint256)',
|
|
80
|
-
'function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)',
|
|
81
|
-
'function ownerOf(uint256 tokenId) view returns (address)',
|
|
82
|
-
'function tokenURI(uint256 tokenId) view returns (string)',
|
|
83
|
-
'function setAgentURI(uint256 agentId, string newURI)',
|
|
84
|
-
'function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)',
|
|
85
|
-
])
|
|
86
|
-
|
|
87
|
-
const REGISTERED_EVENT = parseAbiItem('event Registered(uint256 indexed agentId, address indexed owner, string agentURI)')
|
|
88
|
-
const TRANSFER_EVENT = parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)')
|
|
89
|
-
|
|
90
|
-
type FetchLike = typeof fetch
|
|
91
|
-
type TransferLog = { args: { tokenId?: bigint } }
|
|
92
|
-
type ReceiptLog = { address?: Address; topics: readonly Hex[]; data: Hex }
|
|
93
|
-
type RegisterAgentPreflightClient = {
|
|
94
|
-
estimateGas: (args: { account: Address; to: Address; data: Hex }) => Promise<bigint>
|
|
95
|
-
getGasPrice: () => Promise<bigint>
|
|
96
|
-
getBalance: (args: { address: Address }) => Promise<bigint>
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export type Erc8004RegistryConfig = {
|
|
100
|
-
chainId: number
|
|
101
|
-
rpcUrl: string
|
|
102
|
-
identityRegistryAddress: Address
|
|
103
|
-
fromBlock?: bigint
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export type EthagentBackupPointer = {
|
|
107
|
-
cid: string
|
|
108
|
-
envelopeVersion?: string
|
|
109
|
-
createdAt?: string
|
|
110
|
-
agentAddress?: Address
|
|
111
|
-
pastBackups?: Array<{ cid: string; createdAt?: string }>
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export type EthagentPublicDiscoveryPointer = {
|
|
115
|
-
skillsCid?: string
|
|
116
|
-
agentCardCid?: string
|
|
117
|
-
updatedAt?: string
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export type EthagentRegistrationPointer = {
|
|
121
|
-
chainId: number
|
|
122
|
-
identityRegistryAddress: Address
|
|
123
|
-
agentId?: string
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export type Erc8004AgentCandidate = {
|
|
127
|
-
ownerAddress: Address
|
|
128
|
-
chainId: number
|
|
129
|
-
rpcUrl: string
|
|
130
|
-
identityRegistryAddress: Address
|
|
131
|
-
agentId: bigint
|
|
132
|
-
agentUri: string
|
|
133
|
-
metadataCid?: string
|
|
134
|
-
name?: string
|
|
135
|
-
description?: string
|
|
136
|
-
imageUrl?: string
|
|
137
|
-
backup?: EthagentBackupPointer
|
|
138
|
-
publicDiscovery?: EthagentPublicDiscoveryPointer
|
|
139
|
-
registration: Record<string, unknown> | null
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export type DiscoverOwnedAgentsArgs = Erc8004RegistryConfig & {
|
|
143
|
-
ownerHandle: string
|
|
144
|
-
ipfsApiUrl?: string
|
|
145
|
-
publicClient?: PublicClient
|
|
146
|
-
fetchImpl?: FetchLike
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export type DiscoverOwnedAgentsAcrossSupportedNetworksArgs = {
|
|
150
|
-
ownerHandle: string
|
|
151
|
-
registryOverrides?: Erc8004RegistryConfig[]
|
|
152
|
-
ipfsApiUrl?: string
|
|
153
|
-
publicClients?: Partial<Record<number, PublicClient>>
|
|
154
|
-
fetchImpl?: FetchLike
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export type RegisterAgentPreflight = {
|
|
158
|
-
gas: bigint
|
|
159
|
-
gasPrice: bigint
|
|
160
|
-
estimatedCostWei: bigint
|
|
161
|
-
requiredBalanceWei: bigint
|
|
162
|
-
balanceWei: bigint
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export type RegisterAgentPreflightErrorCode = 'insufficient-funds' | 'simulation-failed'
|
|
166
|
-
|
|
167
|
-
export class RegisterAgentPreflightError extends Error {
|
|
168
|
-
code: RegisterAgentPreflightErrorCode
|
|
169
|
-
title: string
|
|
170
|
-
detail: string
|
|
171
|
-
hint: string
|
|
172
|
-
requiredBalanceWei?: bigint
|
|
173
|
-
balanceWei?: bigint
|
|
174
|
-
|
|
175
|
-
constructor(args: {
|
|
176
|
-
code: RegisterAgentPreflightErrorCode
|
|
177
|
-
title: string
|
|
178
|
-
detail: string
|
|
179
|
-
hint: string
|
|
180
|
-
requiredBalanceWei?: bigint
|
|
181
|
-
balanceWei?: bigint
|
|
182
|
-
}) {
|
|
183
|
-
super(args.title)
|
|
184
|
-
this.name = 'RegisterAgentPreflightError'
|
|
185
|
-
this.code = args.code
|
|
186
|
-
this.title = args.title
|
|
187
|
-
this.detail = args.detail
|
|
188
|
-
this.hint = args.hint
|
|
189
|
-
this.requiredBalanceWei = args.requiredBalanceWei
|
|
190
|
-
this.balanceWei = args.balanceWei
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export class AgentTokenIdRequiredError extends Error {
|
|
195
|
-
ownerAddress: Address
|
|
196
|
-
registry: Erc8004RegistryConfig
|
|
197
|
-
balance: bigint
|
|
198
|
-
detail?: string
|
|
199
|
-
|
|
200
|
-
constructor(args: {
|
|
201
|
-
ownerAddress: Address
|
|
202
|
-
registry: Erc8004RegistryConfig
|
|
203
|
-
balance: bigint
|
|
204
|
-
detail?: string
|
|
205
|
-
}) {
|
|
206
|
-
const chain = supportedErc8004ChainForId(args.registry.chainId)
|
|
207
|
-
const label = chain?.network ?? chain?.name ?? `chain ${args.registry.chainId}`
|
|
208
|
-
super(`Automatic ${label} lookup timed out. Enter the agent token ID to continue.`)
|
|
209
|
-
this.name = 'AgentTokenIdRequiredError'
|
|
210
|
-
this.ownerAddress = args.ownerAddress
|
|
211
|
-
this.registry = args.registry
|
|
212
|
-
this.balance = args.balance
|
|
213
|
-
if (args.detail) this.detail = cleanRpcError(args.detail)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export function createErc8004PublicClient(args: Pick<Erc8004RegistryConfig, 'chainId' | 'rpcUrl'>): PublicClient {
|
|
218
|
-
const transports = rpcUrlsForClient(args).map(url => http(url, { retryCount: 0, timeout: 8_000 }))
|
|
219
|
-
return createPublicClient({
|
|
220
|
-
chain: chainForId(args.chainId),
|
|
221
|
-
transport: transports.length === 1 ? transports[0]! : fallback(transports, { retryCount: 0 }),
|
|
222
|
-
})
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export function supportedErc8004ChainForId(chainId: number): SupportedErc8004Chain | undefined {
|
|
226
|
-
return SUPPORTED_ERC8004_CHAINS.find(chain => chain.chainId === chainId)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function normalizeErc8004RegistryConfig(input: {
|
|
230
|
-
chainId?: number
|
|
231
|
-
rpcUrl?: string
|
|
232
|
-
identityRegistryAddress?: string
|
|
233
|
-
fromBlock?: string | bigint
|
|
234
|
-
}): Erc8004RegistryConfig {
|
|
235
|
-
const chainId = input.chainId ?? DEFAULT_ERC8004_CHAIN_ID
|
|
236
|
-
const chain = supportedErc8004ChainForId(chainId)
|
|
237
|
-
const identityRegistryAddress = input.identityRegistryAddress?.trim() || chain?.identityRegistryAddress
|
|
238
|
-
if (!identityRegistryAddress) throw new MissingRegistryAddressError(chainId)
|
|
239
|
-
if (!isAddress(identityRegistryAddress)) throw new Error('invalid agent registry address')
|
|
240
|
-
let parsedUrl: URL
|
|
241
|
-
try {
|
|
242
|
-
parsedUrl = new URL(input.rpcUrl?.trim() || chain?.rpcUrl || DEFAULT_ETHEREUM_RPC_URL)
|
|
243
|
-
} catch {
|
|
244
|
-
throw new Error('invalid Ethereum RPC URL')
|
|
245
|
-
}
|
|
246
|
-
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
247
|
-
throw new Error('Ethereum RPC URL must be http(s)')
|
|
248
|
-
}
|
|
249
|
-
return {
|
|
250
|
-
chainId,
|
|
251
|
-
rpcUrl: parsedUrl.toString().replace(/\/$/, ''),
|
|
252
|
-
identityRegistryAddress: getAddress(identityRegistryAddress),
|
|
253
|
-
fromBlock: input.fromBlock !== undefined ? BigInt(input.fromBlock) : chain?.fromBlock,
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
export function erc8004ConfigForSupportedChain(chainId: number): Erc8004RegistryConfig {
|
|
258
|
-
const chain = supportedErc8004ChainForId(chainId)
|
|
259
|
-
if (!chain) throw new Error(`unsupported ERC-8004 chain id: ${chainId}`)
|
|
260
|
-
return normalizeErc8004RegistryConfig(chain)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
export async function resolveOwnerHandle(
|
|
264
|
-
ownerHandle: string,
|
|
265
|
-
args: Pick<Erc8004RegistryConfig, 'chainId' | 'rpcUrl'> & { publicClient?: PublicClient },
|
|
266
|
-
): Promise<Address> {
|
|
267
|
-
const trimmed = ownerHandle.trim()
|
|
268
|
-
if (isAddress(trimmed)) return getAddress(trimmed)
|
|
269
|
-
if (!trimmed.includes('.')) throw new Error('enter an Ethereum address or ENS name')
|
|
270
|
-
|
|
271
|
-
const publicClient = args.publicClient ?? createErc8004PublicClient(args)
|
|
272
|
-
const resolved = await publicClient.getEnsAddress({ name: trimmed })
|
|
273
|
-
if (!resolved) throw new Error(`ENS name did not resolve: ${trimmed}`)
|
|
274
|
-
return getAddress(resolved)
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export async function discoverOwnedAgentBackups(args: DiscoverOwnedAgentsArgs): Promise<Erc8004AgentCandidate[]> {
|
|
278
|
-
const publicClient = args.publicClient ?? createErc8004PublicClient(args)
|
|
279
|
-
const ownerAddress = await resolveOwnerHandle(args.ownerHandle, args)
|
|
280
|
-
const fromBlock = args.fromBlock ?? supportedErc8004ChainForId(args.chainId)?.fromBlock ?? 0n
|
|
281
|
-
const tokenIds = await findCandidateTokenIds({
|
|
282
|
-
publicClient,
|
|
283
|
-
registry: args,
|
|
284
|
-
ownerAddress,
|
|
285
|
-
fromBlock,
|
|
286
|
-
})
|
|
287
|
-
const out: Erc8004AgentCandidate[] = []
|
|
288
|
-
for (const tokenId of tokenIds) {
|
|
289
|
-
const candidate = await loadOwnedAgentCandidate({
|
|
290
|
-
...args,
|
|
291
|
-
publicClient,
|
|
292
|
-
ownerAddress,
|
|
293
|
-
tokenId,
|
|
294
|
-
}).catch(err => {
|
|
295
|
-
if (err instanceof TokenOwnerMismatchError) return null
|
|
296
|
-
throw err
|
|
297
|
-
})
|
|
298
|
-
if (candidate) out.push(candidate)
|
|
299
|
-
}
|
|
300
|
-
return out.sort((a, b) => Number(b.agentId - a.agentId))
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
export async function discoverOwnedAgentBackupByTokenId(args: DiscoverOwnedAgentsArgs & {
|
|
304
|
-
tokenId: bigint
|
|
305
|
-
}): Promise<Erc8004AgentCandidate> {
|
|
306
|
-
const publicClient = args.publicClient ?? createErc8004PublicClient(args)
|
|
307
|
-
const ownerAddress = await resolveOwnerHandle(args.ownerHandle, args)
|
|
308
|
-
return loadOwnedAgentCandidate({
|
|
309
|
-
...args,
|
|
310
|
-
publicClient,
|
|
311
|
-
ownerAddress,
|
|
312
|
-
tokenId: args.tokenId,
|
|
313
|
-
})
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export async function discoverOwnedAgentBackupsAcrossSupportedNetworks(
|
|
317
|
-
args: DiscoverOwnedAgentsAcrossSupportedNetworksArgs,
|
|
318
|
-
): Promise<Erc8004AgentCandidate[]> {
|
|
319
|
-
const ownerAddress = await resolveOwnerAddressForSupportedLookup(args)
|
|
320
|
-
const configs = SUPPORTED_ERC8004_CHAINS.map(chain => {
|
|
321
|
-
const override = args.registryOverrides?.find(item => item.chainId === chain.chainId)
|
|
322
|
-
return override ?? erc8004ConfigForSupportedChain(chain.chainId)
|
|
323
|
-
})
|
|
324
|
-
const results = await mapWithConcurrency(configs, DISCOVERY_CONCURRENCY, async config => {
|
|
325
|
-
try {
|
|
326
|
-
return {
|
|
327
|
-
ok: true as const,
|
|
328
|
-
candidates: await discoverOwnedAgentBackups({
|
|
329
|
-
...config,
|
|
330
|
-
ownerHandle: ownerAddress,
|
|
331
|
-
ipfsApiUrl: args.ipfsApiUrl,
|
|
332
|
-
publicClient: args.publicClients?.[config.chainId],
|
|
333
|
-
fetchImpl: args.fetchImpl,
|
|
334
|
-
}),
|
|
335
|
-
}
|
|
336
|
-
} catch (err: unknown) {
|
|
337
|
-
return { ok: false as const, error: err }
|
|
338
|
-
}
|
|
339
|
-
})
|
|
340
|
-
|
|
341
|
-
const candidates = results.flatMap(result => result.ok ? result.candidates : [])
|
|
342
|
-
if (candidates.length > 0) {
|
|
343
|
-
return candidates.sort(compareCandidatesByNetworkThenNewest)
|
|
344
|
-
}
|
|
345
|
-
const failures = results.filter(result => !result.ok)
|
|
346
|
-
if (failures.length === results.length && failures.length > 0) {
|
|
347
|
-
throw new Error(`lookup failed on all supported networks: ${cleanRpcError(failures[0]!.error)}`)
|
|
348
|
-
}
|
|
349
|
-
const tokenIdRequired = failures
|
|
350
|
-
.map(result => result.error)
|
|
351
|
-
.find((err): err is AgentTokenIdRequiredError => err instanceof AgentTokenIdRequiredError)
|
|
352
|
-
if (tokenIdRequired) throw tokenIdRequired
|
|
353
|
-
return []
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
export async function loadAgentRegistration(
|
|
357
|
-
uri: string,
|
|
358
|
-
args: { ipfsApiUrl?: string; fetchImpl?: FetchLike } = {},
|
|
359
|
-
): Promise<{ metadataCid?: string; registration: Record<string, unknown> }> {
|
|
360
|
-
const trimmed = uri.trim()
|
|
361
|
-
let raw: string
|
|
362
|
-
if (trimmed.startsWith('ipfs://')) {
|
|
363
|
-
const cid = cidFromUri(trimmed)
|
|
364
|
-
if (!cid) throw new Error('agentURI is missing an IPFS CID')
|
|
365
|
-
raw = new TextDecoder().decode(await catFromIpfs(args.ipfsApiUrl ?? DEFAULT_IPFS_API_URL, cid))
|
|
366
|
-
return { metadataCid: cid, registration: parseJsonObject(raw) }
|
|
367
|
-
}
|
|
368
|
-
if (trimmed.startsWith('data:')) {
|
|
369
|
-
return { registration: parseJsonObject(decodeDataUri(trimmed)) }
|
|
370
|
-
}
|
|
371
|
-
if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
|
|
372
|
-
const response = await (args.fetchImpl ?? fetch)(trimmed)
|
|
373
|
-
if (!response.ok) throw new Error(`agent metadata fetch failed: ${response.status} ${response.statusText}`)
|
|
374
|
-
return { registration: parseJsonObject(await response.text()) }
|
|
375
|
-
}
|
|
376
|
-
throw new Error('unsupported agentURI scheme')
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
export function parseEthagentBackupPointer(registration: Record<string, unknown> | null): EthagentBackupPointer | null {
|
|
380
|
-
if (!registration) return null
|
|
381
|
-
const ext = objectField(registration, 'x-ethagent') ?? objectField(registration, 'ethagent')
|
|
382
|
-
const backup = ext ? objectField(ext, 'backup') : null
|
|
383
|
-
const cid = backup ? stringField(backup, 'cid') : undefined
|
|
384
|
-
if (!cid) return null
|
|
385
|
-
const agentAddress = stringField(ext, 'agentAddress')
|
|
386
|
-
const pastBackupsArray = arrayField(backup ?? {}, 'pastBackups')
|
|
387
|
-
const pastBackups = pastBackupsArray?.flatMap(item => {
|
|
388
|
-
if (!item || typeof item !== 'object') return []
|
|
389
|
-
const obj = item as Record<string, unknown>
|
|
390
|
-
const itemCid = stringField(obj, 'cid')
|
|
391
|
-
if (!itemCid) return []
|
|
392
|
-
return [{
|
|
393
|
-
cid: itemCid,
|
|
394
|
-
...(stringField(obj, 'createdAt') ? { createdAt: stringField(obj, 'createdAt') } : {}),
|
|
395
|
-
}]
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
return {
|
|
399
|
-
cid,
|
|
400
|
-
envelopeVersion: backup ? stringField(backup, 'envelopeVersion') : undefined,
|
|
401
|
-
createdAt: backup ? stringField(backup, 'createdAt') : undefined,
|
|
402
|
-
...(agentAddress && isAddress(agentAddress) ? { agentAddress: getAddress(agentAddress) } : {}),
|
|
403
|
-
...(pastBackups && pastBackups.length > 0 ? { pastBackups } : {}),
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
export function parseEthagentPublicDiscoveryPointer(registration: Record<string, unknown> | null): EthagentPublicDiscoveryPointer | null {
|
|
408
|
-
if (!registration) return null
|
|
409
|
-
const ext = objectField(registration, 'x-ethagent') ?? objectField(registration, 'ethagent')
|
|
410
|
-
const publicSkills = ext ? objectField(ext, 'publicSkills') : null
|
|
411
|
-
const agentCard = ext ? objectField(ext, 'agentCard') : null
|
|
412
|
-
const skillsCid = publicSkills ? stringField(publicSkills, 'cid') : undefined
|
|
413
|
-
const agentCardCid = agentCard ? stringField(agentCard, 'cid') : undefined
|
|
414
|
-
const updatedAt = (publicSkills ? stringField(publicSkills, 'updatedAt') : undefined)
|
|
415
|
-
?? (agentCard ? stringField(agentCard, 'updatedAt') : undefined)
|
|
416
|
-
if (!skillsCid && !agentCardCid) return null
|
|
417
|
-
return {
|
|
418
|
-
...(skillsCid ? { skillsCid } : {}),
|
|
419
|
-
...(agentCardCid ? { agentCardCid } : {}),
|
|
420
|
-
...(updatedAt ? { updatedAt } : {}),
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
export function withEthagentBackupPointer(
|
|
425
|
-
registration: Record<string, unknown> | null,
|
|
426
|
-
backup: EthagentBackupPointer,
|
|
427
|
-
publicDiscovery?: EthagentPublicDiscoveryPointer,
|
|
428
|
-
registrationPointer?: EthagentRegistrationPointer,
|
|
429
|
-
): Record<string, unknown> {
|
|
430
|
-
return withEthagentPointers(registration, { backup, publicDiscovery, registration: registrationPointer })
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
export function withEthagentPointers(
|
|
434
|
-
registration: Record<string, unknown> | null,
|
|
435
|
-
pointers: {
|
|
436
|
-
backup?: EthagentBackupPointer
|
|
437
|
-
publicDiscovery?: EthagentPublicDiscoveryPointer
|
|
438
|
-
registration?: EthagentRegistrationPointer
|
|
439
|
-
},
|
|
440
|
-
): Record<string, unknown> {
|
|
441
|
-
const next: Record<string, unknown> = registration ? { ...registration } : {}
|
|
442
|
-
const prior = objectField(next, 'x-ethagent') ?? {}
|
|
443
|
-
const { backup, publicDiscovery, registration: registrationPointer } = pointers
|
|
444
|
-
const updatedAt = publicDiscovery?.updatedAt ?? backup?.createdAt
|
|
445
|
-
next['x-ethagent'] = {
|
|
446
|
-
...prior,
|
|
447
|
-
version: 1,
|
|
448
|
-
...(backup?.agentAddress ? { agentAddress: backup.agentAddress } : {}),
|
|
449
|
-
...(backup ? {
|
|
450
|
-
backup: {
|
|
451
|
-
cid: backup.cid,
|
|
452
|
-
...(backup.envelopeVersion ? { envelopeVersion: backup.envelopeVersion } : {}),
|
|
453
|
-
...(backup.createdAt ? { createdAt: backup.createdAt } : {}),
|
|
454
|
-
},
|
|
455
|
-
} : {}),
|
|
456
|
-
...(publicDiscovery?.skillsCid ? {
|
|
457
|
-
publicSkills: {
|
|
458
|
-
cid: publicDiscovery.skillsCid,
|
|
459
|
-
format: 'application/json',
|
|
460
|
-
...(updatedAt ? { updatedAt } : {}),
|
|
461
|
-
},
|
|
462
|
-
} : {}),
|
|
463
|
-
...(publicDiscovery?.agentCardCid ? {
|
|
464
|
-
agentCard: {
|
|
465
|
-
cid: publicDiscovery.agentCardCid,
|
|
466
|
-
format: 'application/json',
|
|
467
|
-
...(updatedAt ? { updatedAt } : {}),
|
|
468
|
-
},
|
|
469
|
-
} : {}),
|
|
470
|
-
}
|
|
471
|
-
if (publicDiscovery) {
|
|
472
|
-
next.services = withPublicDiscoveryServices(next.services, publicDiscovery)
|
|
473
|
-
}
|
|
474
|
-
if (registrationPointer?.agentId) {
|
|
475
|
-
next.registrations = withRegistrationsArray(next.registrations, registrationPointer)
|
|
476
|
-
}
|
|
477
|
-
return next
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
export function encodeRegisterAgent(args: {
|
|
481
|
-
agentURI: string
|
|
482
|
-
}): Hex {
|
|
483
|
-
return encodeFunctionData({
|
|
484
|
-
abi: ERC8004_ABI,
|
|
485
|
-
functionName: 'register',
|
|
486
|
-
args: [args.agentURI],
|
|
487
|
-
})
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
export async function preflightRegisterAgent(args: Erc8004RegistryConfig & {
|
|
491
|
-
ownerAddress: Address
|
|
492
|
-
agentURI: string
|
|
493
|
-
publicClient?: RegisterAgentPreflightClient
|
|
494
|
-
}): Promise<RegisterAgentPreflight> {
|
|
495
|
-
const publicClient = args.publicClient ?? createErc8004PublicClient(args) as RegisterAgentPreflightClient
|
|
496
|
-
const data = encodeRegisterAgent({ agentURI: args.agentURI })
|
|
497
|
-
let gas: bigint
|
|
498
|
-
try {
|
|
499
|
-
gas = await publicClient.estimateGas({
|
|
500
|
-
account: args.ownerAddress,
|
|
501
|
-
to: args.identityRegistryAddress,
|
|
502
|
-
data,
|
|
503
|
-
})
|
|
504
|
-
} catch (err: unknown) {
|
|
505
|
-
throw new RegisterAgentPreflightError({
|
|
506
|
-
code: 'simulation-failed',
|
|
507
|
-
title: 'registration blocked',
|
|
508
|
-
detail: cleanRpcError(err),
|
|
509
|
-
hint: 'No transaction was sent.',
|
|
510
|
-
})
|
|
511
|
-
}
|
|
512
|
-
const [gasPrice, balance] = await Promise.all([
|
|
513
|
-
publicClient.getGasPrice(),
|
|
514
|
-
publicClient.getBalance({ address: args.ownerAddress }),
|
|
515
|
-
])
|
|
516
|
-
const estimatedCost = gas * gasPrice
|
|
517
|
-
const requiredBalance = estimatedCost + estimatedCost / 5n
|
|
518
|
-
if (balance < requiredBalance) {
|
|
519
|
-
throw new RegisterAgentPreflightError({
|
|
520
|
-
code: 'insufficient-funds',
|
|
521
|
-
title: 'not enough ETH',
|
|
522
|
-
detail: `Need ~${formatEthAmount(requiredBalance)} ETH. Wallet has ${formatEthAmount(balance)} ETH.`,
|
|
523
|
-
hint: 'Add ETH to this wallet, then try again.',
|
|
524
|
-
requiredBalanceWei: requiredBalance,
|
|
525
|
-
balanceWei: balance,
|
|
526
|
-
})
|
|
527
|
-
}
|
|
528
|
-
return {
|
|
529
|
-
gas,
|
|
530
|
-
gasPrice,
|
|
531
|
-
estimatedCostWei: estimatedCost,
|
|
532
|
-
requiredBalanceWei: requiredBalance,
|
|
533
|
-
balanceWei: balance,
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
export async function preflightSetAgentUri(args: Erc8004RegistryConfig & {
|
|
538
|
-
account: Address
|
|
539
|
-
agentId: bigint
|
|
540
|
-
newUri: string
|
|
541
|
-
publicClient?: RegisterAgentPreflightClient
|
|
542
|
-
}): Promise<void> {
|
|
543
|
-
const publicClient = args.publicClient ?? createErc8004PublicClient(args) as RegisterAgentPreflightClient
|
|
544
|
-
const data = encodeSetAgentUri({ agentId: args.agentId, newUri: args.newUri })
|
|
545
|
-
try {
|
|
546
|
-
await publicClient.estimateGas({
|
|
547
|
-
account: args.account,
|
|
548
|
-
to: args.identityRegistryAddress,
|
|
549
|
-
data,
|
|
550
|
-
})
|
|
551
|
-
} catch (err: unknown) {
|
|
552
|
-
const detail = cleanRpcError(err)
|
|
553
|
-
const looksLikeOwnershipRevert = /not.*owner|owner.*only|unauthor|forbidden|caller/i.test(detail)
|
|
554
|
-
throw new RegisterAgentPreflightError({
|
|
555
|
-
code: 'simulation-failed',
|
|
556
|
-
title: 'backup update blocked',
|
|
557
|
-
detail,
|
|
558
|
-
hint: looksLikeOwnershipRevert
|
|
559
|
-
? `Connect the wallet that owns this agent (${args.account}) and try again.`
|
|
560
|
-
: 'No transaction was sent.',
|
|
561
|
-
})
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
export function encodeSetAgentUri(args: {
|
|
566
|
-
agentId: bigint
|
|
567
|
-
newUri: string
|
|
568
|
-
}): Hex {
|
|
569
|
-
return encodeFunctionData({
|
|
570
|
-
abi: ERC8004_ABI,
|
|
571
|
-
functionName: 'setAgentURI',
|
|
572
|
-
args: [args.agentId, args.newUri],
|
|
573
|
-
})
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
export function registeredAgentFromReceipt(args: {
|
|
577
|
-
logs: ReceiptLog[]
|
|
578
|
-
identityRegistryAddress: Address
|
|
579
|
-
ownerAddress?: Address
|
|
580
|
-
}): { agentId: bigint; agentURI: string; owner: Address } {
|
|
581
|
-
for (const log of args.logs) {
|
|
582
|
-
if (log.address && log.address.toLowerCase() !== args.identityRegistryAddress.toLowerCase()) continue
|
|
583
|
-
try {
|
|
584
|
-
const decoded = decodeEventLog({
|
|
585
|
-
abi: [REGISTERED_EVENT],
|
|
586
|
-
topics: log.topics as [Hex, ...Hex[]],
|
|
587
|
-
data: log.data,
|
|
588
|
-
})
|
|
589
|
-
if (decoded.eventName !== 'Registered') continue
|
|
590
|
-
const eventArgs = decoded.args as { agentId?: bigint; agentURI?: string; owner?: Address }
|
|
591
|
-
if (eventArgs.agentId === undefined || !eventArgs.agentURI || !eventArgs.owner) continue
|
|
592
|
-
if (args.ownerAddress && eventArgs.owner.toLowerCase() !== args.ownerAddress.toLowerCase()) continue
|
|
593
|
-
return {
|
|
594
|
-
agentId: eventArgs.agentId,
|
|
595
|
-
agentURI: eventArgs.agentURI,
|
|
596
|
-
owner: getAddress(eventArgs.owner),
|
|
597
|
-
}
|
|
598
|
-
} catch {
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
throw new Error('ERC-8004 registration event was not found in transaction receipt')
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
export function cidFromUri(uri: string): string | undefined {
|
|
605
|
-
if (!uri.startsWith('ipfs://')) return undefined
|
|
606
|
-
const withoutScheme = uri.slice('ipfs://'.length)
|
|
607
|
-
return withoutScheme.startsWith('ipfs/') ? withoutScheme.slice('ipfs/'.length) : withoutScheme
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
async function findCandidateTokenIds(args: {
|
|
611
|
-
publicClient: PublicClient
|
|
612
|
-
registry: Erc8004RegistryConfig
|
|
613
|
-
ownerAddress: Address
|
|
614
|
-
fromBlock: bigint
|
|
615
|
-
}): Promise<bigint[]> {
|
|
616
|
-
const tokenIds = new Set<bigint>()
|
|
617
|
-
let balance: bigint | undefined
|
|
618
|
-
let attempt = 0
|
|
619
|
-
while (true) {
|
|
620
|
-
try {
|
|
621
|
-
balance = await args.publicClient.readContract({
|
|
622
|
-
address: args.registry.identityRegistryAddress,
|
|
623
|
-
abi: ERC8004_ABI,
|
|
624
|
-
functionName: 'balanceOf',
|
|
625
|
-
args: [args.ownerAddress],
|
|
626
|
-
}) as bigint
|
|
627
|
-
break
|
|
628
|
-
} catch (err: unknown) {
|
|
629
|
-
if (++attempt > 3) {
|
|
630
|
-
throw new AgentTokenIdRequiredError({
|
|
631
|
-
ownerAddress: args.ownerAddress,
|
|
632
|
-
registry: args.registry,
|
|
633
|
-
balance: 0n,
|
|
634
|
-
detail: cleanRpcError(err),
|
|
635
|
-
})
|
|
636
|
-
}
|
|
637
|
-
await new Promise(r => setTimeout(r, attempt * 1000))
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
if (balance === 0n) return []
|
|
641
|
-
|
|
642
|
-
const enumerableTokenIds = await findEnumerableTokenIds({
|
|
643
|
-
publicClient: args.publicClient,
|
|
644
|
-
registry: args.registry,
|
|
645
|
-
ownerAddress: args.ownerAddress,
|
|
646
|
-
balance,
|
|
647
|
-
})
|
|
648
|
-
if (enumerableTokenIds) return enumerableTokenIds
|
|
649
|
-
|
|
650
|
-
try {
|
|
651
|
-
for await (const logs of getTransferLogChunksBackwards({
|
|
652
|
-
publicClient: args.publicClient,
|
|
653
|
-
registry: args.registry,
|
|
654
|
-
ownerAddress: args.ownerAddress,
|
|
655
|
-
fromBlock: args.fromBlock,
|
|
656
|
-
})) {
|
|
657
|
-
for (const log of logs) {
|
|
658
|
-
const tokenId = log.args.tokenId
|
|
659
|
-
if (tokenId === undefined || tokenIds.has(tokenId)) continue
|
|
660
|
-
if (await isCurrentTokenOwner(args.publicClient, args.registry.identityRegistryAddress, tokenId, args.ownerAddress)) {
|
|
661
|
-
tokenIds.add(tokenId)
|
|
662
|
-
if (BigInt(tokenIds.size) >= balance) return [...tokenIds]
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
} catch (err: unknown) {
|
|
667
|
-
throw new AgentTokenIdRequiredError({
|
|
668
|
-
ownerAddress: args.ownerAddress,
|
|
669
|
-
registry: args.registry,
|
|
670
|
-
balance,
|
|
671
|
-
detail: cleanRpcError(err),
|
|
672
|
-
})
|
|
673
|
-
}
|
|
674
|
-
if (BigInt(tokenIds.size) < balance) {
|
|
675
|
-
throw new AgentTokenIdRequiredError({
|
|
676
|
-
ownerAddress: args.ownerAddress,
|
|
677
|
-
registry: args.registry,
|
|
678
|
-
balance,
|
|
679
|
-
detail: 'owned token ids were not found in logs',
|
|
680
|
-
})
|
|
681
|
-
}
|
|
682
|
-
return [...tokenIds]
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
async function* getTransferLogChunksBackwards(args: {
|
|
686
|
-
publicClient: PublicClient
|
|
687
|
-
registry: Erc8004RegistryConfig
|
|
688
|
-
ownerAddress: Address
|
|
689
|
-
fromBlock: bigint
|
|
690
|
-
}): AsyncGenerator<TransferLog[]> {
|
|
691
|
-
const latest = await args.publicClient.getBlockNumber()
|
|
692
|
-
if (args.fromBlock > latest) return
|
|
693
|
-
|
|
694
|
-
const ranges = blockRangesBackwards(args.fromBlock, latest, logBlockRangeForChain(args.registry.chainId))
|
|
695
|
-
const CONCURRENCY = 5
|
|
696
|
-
|
|
697
|
-
for (let i = 0; i < ranges.length; i += CONCURRENCY) {
|
|
698
|
-
const batch = ranges.slice(i, i + CONCURRENCY)
|
|
699
|
-
const logsArrays = await Promise.all(batch.map(async range => {
|
|
700
|
-
try {
|
|
701
|
-
return await getTransferLogsAdaptive({
|
|
702
|
-
...args,
|
|
703
|
-
fromBlock: range.fromBlock,
|
|
704
|
-
toBlock: range.toBlock,
|
|
705
|
-
minBlockRange: minLogBlockRangeForChain(args.registry.chainId),
|
|
706
|
-
})
|
|
707
|
-
} catch {
|
|
708
|
-
return [] as TransferLog[]
|
|
709
|
-
}
|
|
710
|
-
}))
|
|
711
|
-
for (const logs of logsArrays) {
|
|
712
|
-
if (logs.length > 0) yield logs
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
async function findEnumerableTokenIds(args: {
|
|
718
|
-
publicClient: PublicClient
|
|
719
|
-
registry: Erc8004RegistryConfig
|
|
720
|
-
ownerAddress: Address
|
|
721
|
-
balance: bigint
|
|
722
|
-
}): Promise<bigint[] | null> {
|
|
723
|
-
const tokenIds: bigint[] = []
|
|
724
|
-
try {
|
|
725
|
-
for (let index = 0n; index < args.balance; index += 1n) {
|
|
726
|
-
const tokenId = await args.publicClient.readContract({
|
|
727
|
-
address: args.registry.identityRegistryAddress,
|
|
728
|
-
abi: ERC8004_ABI,
|
|
729
|
-
functionName: 'tokenOfOwnerByIndex',
|
|
730
|
-
args: [args.ownerAddress, index],
|
|
731
|
-
}) as bigint
|
|
732
|
-
tokenIds.push(tokenId)
|
|
733
|
-
}
|
|
734
|
-
return tokenIds
|
|
735
|
-
} catch {
|
|
736
|
-
return null
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
async function getTransferLogsAdaptive(args: {
|
|
741
|
-
publicClient: PublicClient
|
|
742
|
-
registry: Erc8004RegistryConfig
|
|
743
|
-
ownerAddress: Address
|
|
744
|
-
fromBlock: bigint
|
|
745
|
-
toBlock: bigint
|
|
746
|
-
minBlockRange: bigint
|
|
747
|
-
}): Promise<TransferLog[]> {
|
|
748
|
-
const size = args.toBlock - args.fromBlock + 1n
|
|
749
|
-
let attempt = 0
|
|
750
|
-
while (true) {
|
|
751
|
-
try {
|
|
752
|
-
const logs = await args.publicClient.getLogs({
|
|
753
|
-
address: args.registry.identityRegistryAddress,
|
|
754
|
-
event: TRANSFER_EVENT,
|
|
755
|
-
args: { to: args.ownerAddress },
|
|
756
|
-
fromBlock: args.fromBlock,
|
|
757
|
-
toBlock: args.toBlock,
|
|
758
|
-
})
|
|
759
|
-
return logs as TransferLog[]
|
|
760
|
-
} catch (err: unknown) {
|
|
761
|
-
attempt++
|
|
762
|
-
const msg = err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase()
|
|
763
|
-
const isSizeLimit = msg.includes('limit') || msg.includes('range') || msg.includes('too many') || msg.includes('exceeds') || msg.includes('block count')
|
|
764
|
-
|
|
765
|
-
if (!isSizeLimit && attempt <= 3) {
|
|
766
|
-
await new Promise(r => setTimeout(r, attempt * 1000))
|
|
767
|
-
continue
|
|
768
|
-
}
|
|
769
|
-
if (size <= args.minBlockRange) {
|
|
770
|
-
if (attempt <= 3) {
|
|
771
|
-
await new Promise(r => setTimeout(r, attempt * 1000))
|
|
772
|
-
continue
|
|
773
|
-
}
|
|
774
|
-
throw err
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
const mid = args.fromBlock + size / 2n - 1n
|
|
778
|
-
const [newer, older] = await Promise.all([
|
|
779
|
-
getTransferLogsAdaptive({ ...args, fromBlock: mid + 1n, toBlock: args.toBlock }),
|
|
780
|
-
getTransferLogsAdaptive({ ...args, fromBlock: args.fromBlock, toBlock: mid })
|
|
781
|
-
])
|
|
782
|
-
return [...newer, ...older]
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
async function findContractDeploymentBlock(publicClient: PublicClient, address: Address): Promise<bigint> {
|
|
788
|
-
const latest = await publicClient.getBlockNumber()
|
|
789
|
-
const latestCode = await publicClient.getBytecode({ address, blockNumber: latest })
|
|
790
|
-
if (!latestCode || latestCode === '0x') throw new Error(`no contract code at ${address}`)
|
|
791
|
-
|
|
792
|
-
let low = 0n
|
|
793
|
-
let high = latest
|
|
794
|
-
while (low < high) {
|
|
795
|
-
const mid = (low + high) / 2n
|
|
796
|
-
const code = await publicClient.getBytecode({ address, blockNumber: mid })
|
|
797
|
-
if (code && code !== '0x') high = mid
|
|
798
|
-
else low = mid + 1n
|
|
799
|
-
}
|
|
800
|
-
return low
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
class TokenOwnerMismatchError extends Error {
|
|
804
|
-
constructor() {
|
|
805
|
-
super('token is not owned by this wallet')
|
|
806
|
-
this.name = 'TokenOwnerMismatchError'
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
async function loadOwnedAgentCandidate(args: DiscoverOwnedAgentsArgs & {
|
|
811
|
-
publicClient: PublicClient
|
|
812
|
-
ownerAddress: Address
|
|
813
|
-
tokenId: bigint
|
|
814
|
-
}): Promise<Erc8004AgentCandidate> {
|
|
815
|
-
let attempt = 0
|
|
816
|
-
let currentOwner: Address | undefined
|
|
817
|
-
let agentUri: string | undefined
|
|
818
|
-
while (true) {
|
|
819
|
-
try {
|
|
820
|
-
if (!currentOwner) {
|
|
821
|
-
currentOwner = await args.publicClient.readContract({
|
|
822
|
-
address: args.identityRegistryAddress,
|
|
823
|
-
abi: ERC8004_ABI,
|
|
824
|
-
functionName: 'ownerOf',
|
|
825
|
-
args: [args.tokenId],
|
|
826
|
-
}) as Address
|
|
827
|
-
}
|
|
828
|
-
if (!agentUri) {
|
|
829
|
-
agentUri = await args.publicClient.readContract({
|
|
830
|
-
address: args.identityRegistryAddress,
|
|
831
|
-
abi: ERC8004_ABI,
|
|
832
|
-
functionName: 'tokenURI',
|
|
833
|
-
args: [args.tokenId],
|
|
834
|
-
}) as string
|
|
835
|
-
}
|
|
836
|
-
break
|
|
837
|
-
} catch (err: unknown) {
|
|
838
|
-
if (++attempt > 3) throw err
|
|
839
|
-
await new Promise(r => setTimeout(r, attempt * 1000))
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
if (currentOwner.toLowerCase() !== args.ownerAddress.toLowerCase()) {
|
|
844
|
-
throw new TokenOwnerMismatchError()
|
|
845
|
-
}
|
|
846
|
-
const loaded = await loadAgentRegistration(agentUri, {
|
|
847
|
-
ipfsApiUrl: args.ipfsApiUrl ?? DEFAULT_IPFS_API_URL,
|
|
848
|
-
fetchImpl: args.fetchImpl,
|
|
849
|
-
}).catch(() => ({ metadataCid: cidFromUri(agentUri!), registration: null }))
|
|
850
|
-
const parsed = parseEthagentBackupPointer(loaded.registration)
|
|
851
|
-
const publicDiscovery = parseEthagentPublicDiscoveryPointer(loaded.registration)
|
|
852
|
-
return {
|
|
853
|
-
ownerAddress: args.ownerAddress,
|
|
854
|
-
chainId: args.chainId,
|
|
855
|
-
rpcUrl: args.rpcUrl,
|
|
856
|
-
identityRegistryAddress: args.identityRegistryAddress,
|
|
857
|
-
agentId: args.tokenId,
|
|
858
|
-
agentUri,
|
|
859
|
-
metadataCid: loaded.metadataCid,
|
|
860
|
-
name: stringField(loaded.registration, 'name'),
|
|
861
|
-
description: stringField(loaded.registration, 'description'),
|
|
862
|
-
imageUrl: stringField(loaded.registration, 'image'),
|
|
863
|
-
backup: parsed ?? undefined,
|
|
864
|
-
publicDiscovery: publicDiscovery ?? undefined,
|
|
865
|
-
registration: loaded.registration,
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
async function isCurrentTokenOwner(
|
|
870
|
-
publicClient: PublicClient,
|
|
871
|
-
registry: Address,
|
|
872
|
-
tokenId: bigint,
|
|
873
|
-
ownerAddress: Address,
|
|
874
|
-
): Promise<boolean> {
|
|
875
|
-
let attempt = 0
|
|
876
|
-
while (true) {
|
|
877
|
-
try {
|
|
878
|
-
const currentOwner = await publicClient.readContract({
|
|
879
|
-
address: registry,
|
|
880
|
-
abi: ERC8004_ABI,
|
|
881
|
-
functionName: 'ownerOf',
|
|
882
|
-
args: [tokenId],
|
|
883
|
-
}) as Address
|
|
884
|
-
return currentOwner.toLowerCase() === ownerAddress.toLowerCase()
|
|
885
|
-
} catch (err: unknown) {
|
|
886
|
-
if (++attempt > 3) throw err
|
|
887
|
-
await new Promise(r => setTimeout(r, attempt * 1000))
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
async function resolveOwnerAddressForSupportedLookup(
|
|
893
|
-
args: DiscoverOwnedAgentsAcrossSupportedNetworksArgs,
|
|
894
|
-
): Promise<Address> {
|
|
895
|
-
const trimmed = args.ownerHandle.trim()
|
|
896
|
-
if (isAddress(trimmed)) return getAddress(trimmed)
|
|
897
|
-
const mainnetConfig = erc8004ConfigForSupportedChain(mainnet.id)
|
|
898
|
-
return resolveOwnerHandle(trimmed, {
|
|
899
|
-
...mainnetConfig,
|
|
900
|
-
publicClient: args.publicClients?.[mainnet.id],
|
|
901
|
-
})
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
async function mapWithConcurrency<input, output>(
|
|
905
|
-
inputs: input[],
|
|
906
|
-
concurrency: number,
|
|
907
|
-
mapper: (input: input) => Promise<output>,
|
|
908
|
-
): Promise<output[]> {
|
|
909
|
-
const out: output[] = new Array(inputs.length)
|
|
910
|
-
let next = 0
|
|
911
|
-
const workers = Array.from({ length: Math.min(concurrency, inputs.length) }, async () => {
|
|
912
|
-
while (next < inputs.length) {
|
|
913
|
-
const index = next++
|
|
914
|
-
out[index] = await mapper(inputs[index]!)
|
|
915
|
-
}
|
|
916
|
-
})
|
|
917
|
-
await Promise.all(workers)
|
|
918
|
-
return out
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
function compareCandidatesByNetworkThenNewest(a: Erc8004AgentCandidate, b: Erc8004AgentCandidate): number {
|
|
922
|
-
const networkOrder = chainSortIndex(a.chainId) - chainSortIndex(b.chainId)
|
|
923
|
-
if (networkOrder !== 0) return networkOrder
|
|
924
|
-
return Number(b.agentId - a.agentId)
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
function blockRangesBackwards(
|
|
928
|
-
fromBlock: bigint,
|
|
929
|
-
latest: bigint,
|
|
930
|
-
blockRange: bigint,
|
|
931
|
-
): Array<{ fromBlock: bigint; toBlock: bigint }> {
|
|
932
|
-
const ranges: Array<{ fromBlock: bigint; toBlock: bigint }> = []
|
|
933
|
-
for (let end = latest; end >= fromBlock;) {
|
|
934
|
-
const start = end - blockRange + 1n > fromBlock ? end - blockRange + 1n : fromBlock
|
|
935
|
-
ranges.push({ fromBlock: start, toBlock: end })
|
|
936
|
-
if (start === fromBlock) break
|
|
937
|
-
end = start - 1n
|
|
938
|
-
}
|
|
939
|
-
return ranges
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
function logBlockRangeForChain(chainId: number): bigint {
|
|
943
|
-
const chain = supportedErc8004ChainForId(chainId)
|
|
944
|
-
if (!chain) return 10_000n
|
|
945
|
-
return chain.logBlockRange
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
function minLogBlockRangeForChain(chainId: number): bigint {
|
|
949
|
-
const chain = supportedErc8004ChainForId(chainId)
|
|
950
|
-
if (!chain) return 2_000n
|
|
951
|
-
return chain.kind === 'l2' ? chain.logBlockRange : chain.logBlockRange / 2n || 1n
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
function chainSortIndex(chainId: number): number {
|
|
955
|
-
const index = SUPPORTED_ERC8004_CHAINS.findIndex(chain => chain.chainId === chainId)
|
|
956
|
-
return index === -1 ? Number.MAX_SAFE_INTEGER : index
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
function rpcUrlsForClient(args: Pick<Erc8004RegistryConfig, 'chainId' | 'rpcUrl'>): string[] {
|
|
960
|
-
const chain = supportedErc8004ChainForId(args.chainId)
|
|
961
|
-
return uniqueStrings([
|
|
962
|
-
args.rpcUrl,
|
|
963
|
-
...(chain && args.rpcUrl !== chain.rpcUrl ? [chain.rpcUrl] : []),
|
|
964
|
-
...(chain?.fallbackRpcUrls ?? []),
|
|
965
|
-
])
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
function uniqueStrings(values: string[]): string[] {
|
|
969
|
-
const out: string[] = []
|
|
970
|
-
for (const value of values) {
|
|
971
|
-
const normalized = value.trim().replace(/\/$/, '')
|
|
972
|
-
if (normalized && !out.includes(normalized)) out.push(normalized)
|
|
973
|
-
}
|
|
974
|
-
return out
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
function parseJsonObject(raw: string): Record<string, unknown> {
|
|
978
|
-
const parsed = JSON.parse(raw) as unknown
|
|
979
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
980
|
-
throw new Error('agent metadata must be a JSON object')
|
|
981
|
-
}
|
|
982
|
-
return parsed as Record<string, unknown>
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
function decodeDataUri(uri: string): string {
|
|
986
|
-
const comma = uri.indexOf(',')
|
|
987
|
-
if (comma === -1) throw new Error('invalid data URI')
|
|
988
|
-
const meta = uri.slice(0, comma)
|
|
989
|
-
const body = uri.slice(comma + 1)
|
|
990
|
-
return meta.endsWith(';base64')
|
|
991
|
-
? Buffer.from(body, 'base64').toString('utf8')
|
|
992
|
-
: decodeURIComponent(body)
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
function objectField(input: Record<string, unknown>, key: string): Record<string, unknown> | null {
|
|
996
|
-
const value = input[key]
|
|
997
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return null
|
|
998
|
-
return value as Record<string, unknown>
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
function arrayField(input: Record<string, unknown>, key: string): Array<unknown> | null {
|
|
1002
|
-
const value = input[key]
|
|
1003
|
-
return Array.isArray(value) ? value : null
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
function stringField(input: Record<string, unknown> | null, key: string): string | undefined {
|
|
1007
|
-
const value = input?.[key]
|
|
1008
|
-
return typeof value === 'string' && value.length > 0 ? value : undefined
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
function withPublicDiscoveryServices(input: unknown, publicDiscovery: EthagentPublicDiscoveryPointer): unknown[] {
|
|
1012
|
-
const prior = Array.isArray(input) ? input.filter(item => item && typeof item === 'object') : []
|
|
1013
|
-
const services = prior.filter(item => !isEthagentManagedService(item)) as unknown[]
|
|
1014
|
-
if (publicDiscovery.agentCardCid) {
|
|
1015
|
-
const endpoint = `ipfs://${publicDiscovery.agentCardCid}`
|
|
1016
|
-
pushUniqueService(services, {
|
|
1017
|
-
type: 'a2a',
|
|
1018
|
-
name: 'agent-card',
|
|
1019
|
-
endpoint,
|
|
1020
|
-
url: endpoint,
|
|
1021
|
-
})
|
|
1022
|
-
}
|
|
1023
|
-
if (publicDiscovery.skillsCid) {
|
|
1024
|
-
const endpoint = `ipfs://${publicDiscovery.skillsCid}`
|
|
1025
|
-
pushUniqueService(services, {
|
|
1026
|
-
type: 'A2A-skills',
|
|
1027
|
-
name: 'public-skills',
|
|
1028
|
-
endpoint,
|
|
1029
|
-
url: endpoint,
|
|
1030
|
-
})
|
|
1031
|
-
}
|
|
1032
|
-
return services
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
function pushUniqueService(services: unknown[], service: Record<string, string>): void {
|
|
1036
|
-
const duplicate = services.some(item => {
|
|
1037
|
-
if (!item || typeof item !== 'object' || Array.isArray(item)) return false
|
|
1038
|
-
const obj = item as Record<string, unknown>
|
|
1039
|
-
return obj.type === service.type && obj.endpoint === service.endpoint
|
|
1040
|
-
})
|
|
1041
|
-
if (!duplicate) services.push(service)
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
function isEthagentManagedService(item: unknown): boolean {
|
|
1045
|
-
if (!item || typeof item !== 'object' || Array.isArray(item)) return false
|
|
1046
|
-
const obj = item as Record<string, unknown>
|
|
1047
|
-
const type = obj.type
|
|
1048
|
-
const name = obj.name
|
|
1049
|
-
if (type === 'a2a' && (name === undefined || name === 'agent-card')) return true
|
|
1050
|
-
return (type === 'A2A-skills' || type === 'ipfs') && name === 'public-skills'
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
function withRegistrationsArray(_input: unknown, registration: EthagentRegistrationPointer): unknown[] {
|
|
1054
|
-
return [{
|
|
1055
|
-
agentId: registration.agentId,
|
|
1056
|
-
agentRegistry: `eip155:${registration.chainId}:${registration.identityRegistryAddress}`,
|
|
1057
|
-
}]
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
function cleanRpcError(err: unknown): string {
|
|
1061
|
-
const message = err instanceof Error ? err.message : String(err)
|
|
1062
|
-
return message
|
|
1063
|
-
.replace(/\s+/g, ' ')
|
|
1064
|
-
.slice(0, 220)
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
function formatEthAmount(wei: bigint): string {
|
|
1068
|
-
const [whole = '0', fraction = ''] = formatEther(wei).split('.')
|
|
1069
|
-
const trimmedFraction = fraction.slice(0, 6).replace(/0+$/, '')
|
|
1070
|
-
return trimmedFraction ? `${whole}.${trimmedFraction}` : whole
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
function chainEntry(
|
|
1074
|
-
chainId: number,
|
|
1075
|
-
name: string,
|
|
1076
|
-
rpcUrl: string,
|
|
1077
|
-
fallbackRpcUrls: string[],
|
|
1078
|
-
identityRegistryAddress: string | undefined,
|
|
1079
|
-
fromBlock: bigint | undefined,
|
|
1080
|
-
logBlockRange: bigint,
|
|
1081
|
-
kind: SupportedErc8004Chain['kind'],
|
|
1082
|
-
network: SelectableNetwork,
|
|
1083
|
-
): SupportedErc8004Chain {
|
|
1084
|
-
return {
|
|
1085
|
-
chainId,
|
|
1086
|
-
name,
|
|
1087
|
-
rpcUrl: rpcUrl.replace(/\/$/, ''),
|
|
1088
|
-
fallbackRpcUrls: fallbackRpcUrls.map(url => url.replace(/\/$/, '')),
|
|
1089
|
-
...(identityRegistryAddress ? { identityRegistryAddress: getAddress(identityRegistryAddress) } : {}),
|
|
1090
|
-
...(fromBlock !== undefined ? { fromBlock } : {}),
|
|
1091
|
-
logBlockRange,
|
|
1092
|
-
kind,
|
|
1093
|
-
network,
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
function chainForId(chainId: number): Chain | undefined {
|
|
1098
|
-
switch (chainId) {
|
|
1099
|
-
case mainnet.id: return mainnet
|
|
1100
|
-
case arbitrum.id: return arbitrum
|
|
1101
|
-
case base.id: return base
|
|
1102
|
-
case optimism.id: return optimism
|
|
1103
|
-
case polygon.id: return polygon
|
|
1104
|
-
default: return undefined
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
DEFAULT_ERC8004_CHAIN_ID,
|
|
3
|
+
DEFAULT_ERC8004_IDENTITY_REGISTRY_ADDRESS,
|
|
4
|
+
DEFAULT_ETHEREUM_RPC_URL,
|
|
5
|
+
MissingRegistryAddressError,
|
|
6
|
+
SUPPORTED_ERC8004_CHAINS,
|
|
7
|
+
chainIdForNetwork,
|
|
8
|
+
erc8004ConfigForSupportedChain,
|
|
9
|
+
networkForChainId,
|
|
10
|
+
normalizeErc8004RegistryConfig,
|
|
11
|
+
supportedErc8004ChainForId,
|
|
12
|
+
} from './erc8004/chains.js'
|
|
13
|
+
export type { SupportedErc8004Chain } from './erc8004/chains.js'
|
|
14
|
+
export { createErc8004PublicClient } from './erc8004/client.js'
|
|
15
|
+
export {
|
|
16
|
+
AgentTokenIdRequiredError,
|
|
17
|
+
discoverOwnedAgentBackupByTokenId,
|
|
18
|
+
discoverOwnedAgentBackups,
|
|
19
|
+
discoverOwnedAgentBackupsAcrossSupportedNetworks,
|
|
20
|
+
} from './erc8004/discovery.js'
|
|
21
|
+
export {
|
|
22
|
+
parseEthagentBackupPointer,
|
|
23
|
+
parseEthagentOperatorsPointer,
|
|
24
|
+
parseEthagentPublicDiscoveryPointer,
|
|
25
|
+
withEthagentBackupPointer,
|
|
26
|
+
withEthagentPointers,
|
|
27
|
+
} from './erc8004/metadata.js'
|
|
28
|
+
export {
|
|
29
|
+
RegisterAgentPreflightError,
|
|
30
|
+
preflightRegisterAgent,
|
|
31
|
+
preflightSetAgentUri,
|
|
32
|
+
} from './erc8004/preflight.js'
|
|
33
|
+
export type { RegisterAgentPreflight, RegisterAgentPreflightErrorCode } from './erc8004/preflight.js'
|
|
34
|
+
export type {
|
|
35
|
+
ApprovedOperatorWalletRecord,
|
|
36
|
+
DiscoverOwnedAgentsAcrossSupportedNetworksArgs,
|
|
37
|
+
DiscoverOwnedAgentsArgs,
|
|
38
|
+
Erc8004AgentCandidate,
|
|
39
|
+
Erc8004RegistryConfig,
|
|
40
|
+
EthagentBackupPointer,
|
|
41
|
+
EthagentOperatorsPointer,
|
|
42
|
+
EthagentPublicDiscoveryPointer,
|
|
43
|
+
EthagentRegistrationPointer,
|
|
44
|
+
EthagentX402Pointer,
|
|
45
|
+
} from './erc8004/types.js'
|
|
46
|
+
export { cidFromUri, loadAgentRegistration } from './erc8004/uri.js'
|
|
47
|
+
export { validateErc8004TokenOwner } from './erc8004/ownership.js'
|
|
48
|
+
export type { Erc8004TokenOwnerValidation } from './erc8004/ownership.js'
|
|
49
|
+
export {
|
|
50
|
+
encodeRegisterAgent,
|
|
51
|
+
encodeSetAgentUri,
|
|
52
|
+
registeredAgentFromReceipt,
|
|
53
|
+
} from './erc8004/transactions.js'
|