Repository navigation
Agent Registration Workflow
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
This document explains the complete agent registration workflow in Sigvara, from agent initialization to successful on-chain registration. It covers:
- DID generation using
formatDid()andcomputeDidHash(). - Signature generation required for registration.
- Smart contract integration through
registerAgent(). - Step-by-step examples showing agent creation, DID computation, signature generation, and transaction submission.
- Common failures, gas optimization strategies, and error handling patterns.
- The relationship between agent addresses, DIDs, and on-chain identity records.
The workflow is implemented across TypeScript SDK utilities and a Solidity identity registry. Clients create an agent with an Ed25519 keypair, compute a deterministic DID and its hash, sign a registration digest with the agent’s Ethereum address, and submit a transaction that registers the agent as PendingBond.
The relevant implementation spans two layers:
- TypeScript SDK layer: DID formatting, agent abstraction, and registration helper.
- On-chain Solidity layer: identity registry, DID derivation, signature verification, and state management.
graph TB
subgraph "TypeScript SDK"
did["did.ts<br/>formatDid(), computeDidHash()"]
agent["agent.ts<br/>SigvaraAgent"]
verifier["verifier.ts<br/>registerAgent()"]
end
subgraph "On-chain Identity Registry"
identity["SigvaraIdentity.sol<br/>registerAgent(), computeDidHash()<br/>registrationDigest(), verifyRegistration()"]
end
did --> agent
agent --> verifier
verifier --> identity
Diagram sources
Section sources
- DID utilities:
-
formatDid(agentAddress, chainId)produces the canonical DID string. -
computeDidHash(agentAddress, chainId)computes the keccak256 hash used as the primary index.
-
- Agent abstraction:
-
SigvaraAgentencapsulates an Ed25519 keypair, agent address, chain ID, and exposesdid,didHash, andpublicKeyBytes32.
-
- Registration helper:
-
registerAgent(signer, agentAddress, ed25519PubKeyBytes32, identityAddress, proof)submits the on-chain registration transaction and returnsdidHashandtxHash.
-
- Identity registry:
-
registerAgent(agentAddress, ed25519PubKey, signature)validates the signature, computesdidHash, stores the identity, and emitsAgentRegistered. -
computeDidHash(agentAddress)reproduces the same hash on-chain. -
registrationDigest(agentAddress, operator, ed25519PubKey)defines the signed message structure. -
verifyRegistration(...)checks the signature against the agent address.
-
Section sources
- did.ts:14-28
- agent.ts:43-68
- verifier.ts:265-305
- SigvaraIdentity.sol:240-321
- SigvaraIdentity.sol:560-562
The registration flow binds three identities together:
- Agent Ethereum address: proves control via EOA or ERC-1271 contract signature.
- Ed25519 public key: stored on-chain for off-chain challenge-response authentication.
- Operator: the caller who controls stake and operational actions.
sequenceDiagram
participant Client as "Client App"
participant AgentObj as "SigvaraAgent"
participant Verifier as "registerAgent()"
participant Contract as "SigvaraIdentity.registerAgent()"
participant Chain as "Blockchain State"
Client->>AgentObj : Create agent (Ed25519 keypair + agentAddress + chainId)
AgentObj-->>Client : did, didHash, publicKeyBytes32
Client->>Verifier : Prepare registration parameters
Verifier->>Contract : registrationDigest(agentAddress, operator, ed25519PubKey)
Verifier->>Verifier : Sign digest with agentSigner
Verifier->>Contract : registerAgent(agentAddress, ed25519PubKey, signature)
Contract->>Contract : verifyRegistration(...)
Contract->>Contract : computeDidHash(agentAddress)
Contract->>Chain : Store AgentIdentity(status=PendingBond)
Contract-->>Verifier : Emit AgentRegistered(didHash)
Verifier-->>Client : { didHash, txHash }
Diagram sources
-
formatDid(agentAddress, chainId)constructs the DID stringdid:sigvara:<chainId>:<agentAddress>. -
computeDidHash(agentAddress, chainId)hashes the same components using keccak256, mirroring the on-chain function. - On-chain,
computeDidHash(agentAddress)usesblock.chainidand the agent address to produce the same hash deterministically.
flowchart TD
Start(["Start"]) --> Input["Inputs: agentAddress, chainId"]
Input --> Format["formatDid(): build 'did:sigvara:<chainId>:<address>'"]
Input --> Hash["computeDidHash(): keccak256('did:sigvara:', chainId, ':', agentAddress)"]
Format --> OutputDid["Output: DID string"]
Hash --> OutputHash["Output: bytes32 didHash"]
OutputDid --> End(["End"])
OutputHash --> End
Diagram sources
Section sources
-
SigvaraAgent.generate(options)creates a random Ed25519 seed and wraps it into an agent instance. - Exposes:
-
agentAddress: the Ethereum address controlling the agent. -
did: formatted DID string. -
didHash: deterministic hash of the DID. -
publicKeyBytes32: raw 32-byte Ed25519 public key for on-chain storage.
-
classDiagram
class SigvaraAgent {
-keyPair
-_agentAddress
-_chainId
-_challengeTtl
+generate(options)
+agentAddress
+did
+didHash
+publicKeyBytes32
+issueChallenge(peerDid, ttlSeconds)
+signChallengeFor(payload, audience)
}
Diagram sources
Section sources
- The contract exposes
registrationDigest(agentAddress, operator, ed25519PubKey), which encodes:- Domain separator including the struct name.
-
block.chainid. - Registry address.
-
agentAddress. -
operator. -
ed25519PubKey.
- The client must sign this digest with the agent’s Ethereum private key (EOA or ERC-1271).
- The SDK helper
registerAgent()retrieves the digest from the contract and signs it using the providedagentSigner. If the caller already has a precomputed signature, they can pass it directly.
sequenceDiagram
participant Client as "Client"
participant Verifier as "registerAgent()"
participant Contract as "SigvaraIdentity"
participant Signer as "Agent Signer"
Client->>Verifier : Call registerAgent(signer, agentAddress, ed25519PubKey, identityAddress, proof)
Verifier->>Contract : registrationDigest(agentAddress, operator, ed25519PubKey)
Contract-->>Verifier : Return UNPREFIXED digest
Verifier->>Signer : signMessage(raw digest bytes)
Signer-->>Verifier : Return signature
Verifier->>Contract : registerAgent(agentAddress, ed25519PubKey, signature)
Diagram sources
Section sources
- Reverts with
InflowsPausedUntilwhile a guardian pause is running. - Validates inputs: non-zero agent address and non-zero public key.
- Verifies the signature using
verifyRegistration(), which applies EIP-191 prefixing and supports EOA and ERC-1271 contracts. - Computes
didHashviacomputeDidHash(agentAddress). - Ensures the DID has not been registered before.
- Stores
AgentIdentitywith statusPendingBond, sets the caller asoperator, and emitsAgentRegistered.
flowchart TD
Start(["registerAgent(agentAddress, ed25519PubKey, signature)"]) --> Validate["Validate inputs<br/>non-zero agentAddress and pubKey"]
Validate --> Verify["verifyRegistration(agentAddress, operator, ed25519PubKey, signature)"]
Verify --> Valid{"Signature valid?"}
Valid --> |No| RevertSig["Revert BadRegistrationSignature"]
Valid --> |Yes| Compute["computeDidHash(agentAddress)"]
Compute --> CheckDup{"Already registered?"}
CheckDup --> |Yes| RevertDup["Revert AlreadyRegistered"]
CheckDup --> |No| Store["Store AgentIdentity(status=PendingBond)<br/>set operator=msg.sender"]
Store --> Emit["Emit AgentRegistered(didHash, operator, agentAddress, ed25519PubKey)"]
Emit --> End(["Return didHash"])
Diagram sources
Section sources
Below are step-by-step flows without embedding code content. Use the referenced files for exact implementations.
-
Agent Creation
- Generate an Ed25519 keypair and wrap it in
SigvaraAgent. - Provide
agentAddressandchainId. - Retrieve
publicKeyBytes32,did, anddidHash.
References:
- Generate an Ed25519 keypair and wrap it in
-
DID Computation
- Compute
didviaformatDid(agentAddress, chainId). - Compute
didHashviacomputeDidHash(agentAddress, chainId).
References:
- Compute
-
Signature Generation
- Obtain
registrationDigest(agentAddress, operator, ed25519PubKey)from the contract. - Sign the returned digest with the agent’s Ethereum private key.
- Alternatively, provide a precomputed signature if signing externally.
References:
- Obtain
-
Transaction Submission
- Call
registerAgent(agentAddress, ed25519PubKey, signature)from the operator wallet. - Wait for the transaction receipt and parse the
AgentRegisteredevent to obtaindidHash.
References:
- Call
- Agent address forms part of the DID and determines the
didHash. -
didHashis the primary key for storingAgentIdentityon-chain. -
AgentIdentityincludes:-
operator: the controller of stake and operations. -
agentAddress: the claimed agent address. -
ed25519PubKey: the cryptographic key used for off-chain authentication. -
status: starts asPendingBond; becomesActiveafter sufficient stake. -
registeredAt: timestamp of registration.
-
erDiagram
AGENT_ADDRESS {
address value PK
}
DID_HASH {
bytes32 value PK
}
AGENT_IDENTITY {
bytes32 didHash PK
address operator
address agentAddress
bytes32 ed25519PubKey
enum status
uint256 registeredAt
}
AGENT_ADDRESS ||--o{ DID_HASH : "forms"
DID_HASH ||--o{ AGENT_IDENTITY : "indexes"
Diagram sources
Section sources
The registration process depends on:
- DID utilities for deterministic identity representation.
- Agent abstraction for key management and metadata.
- Verifier helper for digest retrieval, signing, and transaction submission.
- Identity registry for validation, hashing, and state persistence.
graph LR
did["did.ts"] --> agent["agent.ts"]
agent --> verifier["verifier.ts"]
verifier --> identity["SigvaraIdentity.sol"]
Diagram sources
Section sources
- Gas optimization strategies:
- Precompute signatures off-chain when possible; pass them directly to
registerAgent()to avoid extra calls. - Batch operations where feasible (e.g., multiple registrations in separate transactions but prepared offline).
- Avoid unnecessary reads by caching
registrationDigestonly when needed; prefer direct signature passing for HSM or Safe scenarios.
- Precompute signatures off-chain when possible; pass them directly to
- View functions like
computeDidHashandregistrationDigestare cheap and help clients validate data before submitting transactions. - Event parsing occurs post-transaction; ensure efficient log filtering to minimize overhead.
Common registration failures and resolutions:
- Invalid signature:
- Cause: Wrong digest, wrong signer, or double-prefixing issues.
- Resolution: Ensure the digest is retrieved from the contract and signed with the agent’s Ethereum private key. Do not pass hex strings to
signMessage; use raw bytes. - References:
- Duplicate registration:
- Cause: Attempting to register an existing
didHash. - Resolution: Check
getIdentity(didHash)before registering; reuse existing DID if present. - References:
- Cause: Attempting to register an existing
- Zero inputs:
- Cause: Passing zero address or zero public key.
- Resolution: Validate inputs before calling
registerAgent(). - References:
- Registration paused:
- Cause: A guardian pause is running and
registerAgent()reverts withInflowsPausedUntil. A pause ends by itself within three days. - Resolution: Retry after the time in the error, or once the guardian or admin lifts the pause.
- Cause: A guardian pause is running and
- Stake requirements:
- Cause: Agent remains
PendingBonduntil sufficient stake is deposited. - Resolution: Deposit stake of at least
minimumStaketo transition toActive. The deposit that reaches it activates the agent; no separate status call is needed. - References:
- Cause: Agent remains
Section sources
The Sigvara agent registration workflow ensures trustless, deterministic identity anchoring by binding an Ethereum address, an Ed25519 public key, and a controlled operator. Clients generate a DID and its hash, sign a contract-defined registration digest with the agent’s Ethereum key, and submit a transaction that stores the identity as PendingBond. Subsequent staking transitions the agent to Active. Proper signature handling, input validation, and stake management are essential for successful registration and long-term operation.
- Agent Registration and Lifecycle
- Contract Overview and Architecture
- Economic Security Model and Incentives
- Evidence Verification and Merkle Proofs
- Key Management and Rotation
- Maturity Mechanism and Score Release
- Proposal Workflow and Challenge System
- Score Storage and Data Model
- SigvaraIdentity Contract
- SigvaraOracleBond Contract
- SigvaraReputation Contract
- SigvaraStaking Contract
- Slashing Lifecycle and Dispute Resolution
- Stake Management Functions
- Status Management and Control
- Token and Fee Contracts
- Unbonding Period and Withdrawal Security
- Anti-Manipulation and Security Measures
- Community Flag Processing
- Evidence Sources and Verification
- External Trust Integration
- Merkle Proof System
- Oracle Score Computation
- Payment-Backed Attestations
- Scoring Model and Formulas
- Agent Creation and Key Management
- Agent Operations
- Agent Registration Workflow
- Challenge-Response Authentication
- Gate Access Control
- Integration Patterns and Examples
- SDK Overview and Setup
- Verifier Functions