Skip to content

Conversation

@SergeyG-Solicy
Copy link
Contributor

@SergeyG-Solicy SergeyG-Solicy commented Jan 9, 2026

PR Type

Enhancement


Description

  • Added ERC-8004 agent identity support with verification and point system integration

  • Implemented agent identity manager for on-chain NFT ownership verification

  • Extended point system to award/deduct points for agent linking/unlinking

  • Added agent identity storage and retrieval in GCR database

  • Integrated agent identity operations into identity request handling


Diagram Walkthrough

flowchart LR
  A["User with ERC-8004 Agent NFT"] -->|"Sign Ownership Proof"| B["AgentIdentityManager"]
  B -->|"Verify Proof Signature"| C["Signature Verification"]
  B -->|"Verify On-Chain Ownership"| D["Base Sepolia RPC"]
  C -->|"Valid"| E["GCRIdentityRoutines"]
  D -->|"Owner Confirmed"| E
  E -->|"Store Agent Identity"| F["GCR Database"]
  E -->|"Award Points"| G["PointSystem"]
  G -->|"Update Breakdown"| F
  H["User Unlinks Agent"] -->|"Deduct Points"| G
Loading

File Walkthrough

Relevant files
Enhancement
PointSystem.ts
Add agent identity point system integration                           

src/features/incentive/PointSystem.ts

  • Added LINK_AGENT point value (2 points) to point values configuration
  • Extended getUserIdentitiesFromGCR return type to include linkedAgents
    structure with agent details
  • Integrated AgentIdentityManager to fetch agent identities for users
  • Added agent identity processing logic to populate linkedAgents mapping
    by chain
  • Extended getUserPointsInternal to include linkedAgents in returned
    user points
  • Updated addPointsToGCR to support "agents" type for point breakdown
    tracking
  • Implemented awardAgentPoints method to award points when agent is
    linked with duplicate prevention
  • Implemented deductAgentPoints method to deduct points when agent is
    unlinked
+218/-13
GCRIdentityRoutines.ts
Implement agent identity GCR operations                                   

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts

  • Added applyAgentIdentityAdd method to handle agent identity linking
    with validation
  • Added applyAgentIdentityRemove method to handle agent identity
    unlinking
  • Extended isFirstConnection method to support "agent" type with
    database query for duplicate detection
  • Updated applyIdentityEdit switch statement to route "agentadd" and
    "agentremove" operations
  • Integrated IncentiveManager hooks for agent linking/unlinking point
    awards
  • Added validation for EVM address format and proof structure in agent
    identity operations
+196/-2 
IncentiveManager.ts
Add agent identity incentive hooks                                             

src/libs/blockchain/gcr/gcr_routines/IncentiveManager.ts

  • Added agentLinked hook method to award points after agent identity
    linking
  • Added agentUnlinked hook method to deduct points after agent identity
    unlinking
  • Both methods delegate to PointSystem for point calculation and storage
+28/-0   
agentIdentityManager.ts
Create agent identity manager with verification                   

src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts

  • Created new manager class for ERC-8004 agent identity verification and
    storage
  • Implemented verifyAgentOwnership to check on-chain NFT ownership via
    Base Sepolia RPC with failover support
  • Implemented verifyOwnershipProof to validate Demos signature-based
    ownership proof
  • Implemented verifyPayload to orchestrate full verification flow
    including proof and on-chain checks
  • Implemented getAgentIdentities to retrieve agent identities for a
    Demos address with optional chain filtering
  • Implemented getIdentities helper for generic identity retrieval
  • Implemented getConfig to expose agent registry configuration
  • Configured ERC-8004 registry address and Base Sepolia RPC endpoints
    with failover
+354/-0 
identityManager.ts
Extend identity manager for agent support                               

src/libs/blockchain/gcr/gcr_routines/identityManager.ts

  • Extended getIdentities method to support "agent" identity type
  • Added getAgentIdentities method to retrieve agent identities with
    optional chain filtering
  • Updated method signature to include "agent" in identity key union type
+30/-6   
handleIdentityRequest.ts
Integrate agent identity request handling                               

src/libs/network/routines/transactions/handleIdentityRequest.ts

  • Added import for AgentIdentityManager and AgentIdentityAssignPayload
  • Added case handler for "agent_identity_assign" operation type
  • Added case handler for "agent_identity_remove" operation type
  • Integrated agent identity verification into transaction request
    handling pipeline
+10/-1   
GCR_Main.ts
Add agents field to point breakdown                                           

src/model/entities/GCRv2/GCR_Main.ts

  • Added agents field to points breakdown structure for tracking
    agent-specific point allocations
  • Field is optional for backward compatibility with historical records
+1/-0     
IdentityTypes.ts
Define agent identity types and interfaces                             

src/model/entities/types/IdentityTypes.ts

  • Added DemosOwnershipProof interface for signature-based ownership
    verification
  • Added SavedAgentIdentity interface to represent stored ERC-8004 agent
    identities with metadata
  • Added AgentIdentityPayload interface for agent identity assignment
    payload
  • Added AgentIdentityAssignPayload interface for transaction payload
    wrapper
  • Added AgentIdentityRemovePayload interface for agent identity removal
    payload
  • Extended StoredIdentities type to include agent field mapping chains
    to agent identity arrays
+69/-0   

Summary by CodeRabbit

  • New Features
    • Support for linking/unlinking ERC-8004 agent identities with automatic point awards and deductions.
    • Point tracking and user breakdowns now include linked agents across multiple chains.
    • Agent identity verification, registry ownership checks, and management interfaces for fetching a user's agent identities.
    • Incentive lifecycle integration: first-agent-link awards points; agent removal deducts points.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

Walkthrough

Adds ERC-8004 agent identity support across identity verification, GCR persistence, and the incentive/point system, including new agent identity manager, lifecycle handlers, point award/deduction methods, and type/model extensions to track per-agent points.

Changes

Cohort / File(s) Summary
Point System Agent Support
src/features/incentive/PointSystem.ts
Added awardAgentPoints() and deductAgentPoints(); propagate linkedAgents in identity/points responses; extended addPointsToGCR() to accept "agents"; added LINK_AGENT point handling and agent breakdown tracking.
Agent Identity Manager
src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts
New AgentIdentityManager class for payload verification, on‑chain ownership checks, proof verification, and retrieval of persisted agent identities.
GCR Identity Lifecycle
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts
Added applyAgentIdentityAdd() and applyAgentIdentityRemove(); wired agent cases into apply switch; extended isFirstConnection() for agent; awards/deductions via IncentiveManager on first add/remove.
Incentive Hooks
src/libs/blockchain/gcr/gcr_routines/IncentiveManager.ts
Added agentLinked() and agentUnlinked() that delegate to PointSystem agent point methods.
Identity Manager Integration
src/libs/blockchain/gcr/gcr_routines/identityManager.ts
getIdentities() now accepts "agent"; added getAgentIdentities() helper with optional chain filtering.
Network Handler
src/libs/network/routines/transactions/handleIdentityRequest.ts
Added support for agent_identity_assign and agent_identity_remove cases, delegating verification to AgentIdentityManager.
Data Model
src/model/entities/GCRv2/GCR_Main.ts
Added optional agents?: { [agentKey: string]: number } to points breakdown for per-agent tracking.
Types
src/model/entities/types/IdentityTypes.ts
Added SavedAgentIdentity and agent-related payload/proof types; extended StoredIdentities to include agent: { [chain: string]: SavedAgentIdentity[] }.

Sequence Diagram(s)

sequenceDiagram
    participant Sender as Sender/Client
    participant Handler as handleIdentityRequest
    participant AgentMgr as AgentIdentityManager
    participant GCRR as GCRIdentityRoutines
    participant Incentive as IncentiveManager
    participant PointSys as PointSystem
    participant DB as Database

    Sender->>Handler: agent_identity_assign request
    Handler->>AgentMgr: verifyPayload(payload)
    AgentMgr->>DB: on-chain ownership check & proof verification
    AgentMgr-->>Handler: payload verified
    Handler->>GCRR: applyAgentIdentityAdd()
    GCRR->>DB: persist SavedAgentIdentity
    GCRR-->>Incentive: agentLinked(userId, agentId, chain, referral?)
    Incentive->>PointSys: awardAgentPoints(...)
    PointSys->>DB: addPointsToGCR("agents")
    DB-->>PointSys: updated totals
    PointSys-->>Handler: return updated totals
    Handler-->>Sender: success
Loading
sequenceDiagram
    participant Sender as Sender/Client
    participant Handler as handleIdentityRequest
    participant GCRR as GCRIdentityRoutines
    participant Incentive as IncentiveManager
    participant PointSys as PointSystem
    participant DB as Database

    Sender->>Handler: agent_identity_remove request
    Handler->>GCRR: applyAgentIdentityRemove()
    GCRR->>DB: remove SavedAgentIdentity
    GCRR-->>Incentive: agentUnlinked(userId, agentId, chain)
    Incentive->>PointSys: deductAgentPoints(...)
    PointSys->>DB: addPointsToGCR("agents") (subtract)
    DB-->>PointSys: updated totals
    PointSys-->>Handler: return updated totals
    Handler-->>Sender: success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

Possible security concern

Suggested reviewers

  • cwilvx

Poem

🐰 A rabbit hops with ledger cheer,
Chains and proofs now drawing near.
Agents linked and points bestowed,
Across the GCR their footprints show—
A tiny hop for crypto-kind, hooray! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main feature added: ERC-8004 agent identity support, which is the primary objective across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/erc-8004-agent-identity

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • ERC-8004: Entity not found: Issue - Could not find referenced Issue.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 9, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Replay/integrity gaps

Description: The new agent linking verification checks the Demos signature and on-chain ownership but
does not enforce proof freshness/non-replay (e.g., validating proof.timestamp against a
max age or binding the proof to a server-issued nonce) and it also does not validate/lock
payload.payload.chain to the chain actually verified (ownership is always checked on Base
Sepolia), enabling replayable proofs and/or storing agent identities under an arbitrary
chain key that was not actually verified.
agentIdentityManager.ts [112-296]

Referred Code
static async verifyOwnershipProof(
    proof: DemosOwnershipProof,
    sender: string,
): Promise<{ success: boolean; message: string }> {
    try {
        // Verify the proof type
        if (proof.type !== "demos-signature") {
            return {
                success: false,
                message: `Invalid proof type: ${proof.type}, expected "demos-signature"`,
            }
        }

        // Verify the message contains the correct Demos public key
        // Expected format: "I authorize EVM address {evmAddress} to register an ERC-8004 agent for Demos identity {demosPublicKey}..."
        const demosIdentityRegex =
            /for Demos identity (?:0x)?([a-fA-F0-9]+)/
        const match = proof.message.match(demosIdentityRegex)

        if (!match) {
            return {


 ... (clipped 164 lines)
Unbounded data storage

Description: Agent identity linking stores user-controlled fields (tokenUri, resolverUrl, and the full
proof.message/proof.signature) directly into the GCR record without any visible
length/format constraints, which could be exploited to bloat database rows
(storage/processing DoS) by submitting excessively large strings/objects.
GCRIdentityRoutines.ts [701-856]

Referred Code
static async applyAgentIdentityAdd(
    editOperation: any,
    gcrMainRepository: Repository<GCRMain>,
    simulate: boolean,
): Promise<GCRResult> {
    const payload = editOperation.data as AgentIdentityPayload

    // Validate required fields
    if (
        !payload.agentId ||
        !payload.evmAddress ||
        !payload.chain ||
        !payload.proof
    ) {
        return {
            success: false,
            message: "Invalid edit operation data: missing required fields (agentId, evmAddress, chain, proof)",
        }
    }

    // Validate EVM address format


 ... (clipped 135 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: 🏷️
Missing audit logs: Agent identity link/unlink operations persist identity data and trigger point changes
without recording an audit log containing user ID, action details, and outcome.

Referred Code
// SECTION Agent Identity Routines
static async applyAgentIdentityAdd(
    editOperation: any,
    gcrMainRepository: Repository<GCRMain>,
    simulate: boolean,
): Promise<GCRResult> {
    const payload = editOperation.data as AgentIdentityPayload

    // Validate required fields
    if (
        !payload.agentId ||
        !payload.evmAddress ||
        !payload.chain ||
        !payload.proof
    ) {
        return {
            success: false,
            message: "Invalid edit operation data: missing required fields (agentId, evmAddress, chain, proof)",
        }
    }



 ... (clipped 135 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: 🏷️
Missing input checks: External inputs (chain, agentId, proof.signature.type, and proof.evmAddress) are not fully
validated (e.g., allowed chain values, numeric token ID format, supported signature
algorithms), which can lead to runtime errors or unintended verification behavior.

Referred Code
static async verifyPayload(
    payload: AgentIdentityAssignPayload,
    sender: string,
): Promise<{ success: boolean; message: string }> {
    try {
        const { agentId, evmAddress, chain, txHash, tokenUri, proof } =
            payload.payload

        // Validate required fields
        if (!agentId) {
            return {
                success: false,
                message: "Agent ID is required",
            }
        }

        if (!evmAddress) {
            return {
                success: false,
                message: "EVM address is required",
            }


 ... (clipped 63 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: 🏷️
Sensitive error details: User-facing failure messages include internal details such as RPC endpoint failures and
ownership/address specifics, and some error paths return interpolated raw exceptions,
potentially exposing internal implementation details.

Referred Code
static async verifyAgentOwnership(
    agentId: string,
    expectedOwner: string,
): Promise<{ success: boolean; message: string; actualOwner?: string }> {
    for (const rpcUrl of BASE_SEPOLIA_RPC_ENDPOINTS) {
        try {
            const provider = new JsonRpcProvider(rpcUrl)
            const contract = new ethers.Contract(
                AGENT_REGISTRY_ADDRESS,
                registryAbi,
                provider,
            )

            const owner = await contract.ownerOf(agentId)

            if (owner.toLowerCase() !== expectedOwner.toLowerCase()) {
                return {
                    success: false,
                    message: `Agent NFT ${agentId} is owned by ${owner}, not ${expectedOwner}`,
                    actualOwner: owner,
                }


 ... (clipped 20 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: 🏷️
Logs expose identifiers: Logging statements include potentially sensitive identifiers (Demos identity sender,
evmAddress, and agentId) and also log raw error values, which may leak internal or
user-supplied data into logs.

Referred Code
            log.debug(
                `Failed to verify agent ownership via ${rpcUrl}: ${error}`,
            )
            continue
        }
    }

    return {
        success: false,
        message: `Failed to verify agent ownership on all RPC endpoints. Agent ${agentId} may not exist.`,
    }
}

/**
 * Verify Demos ownership proof signature
 *
 * The proof contains:
 * - message: "I authorize EVM address {evmAddress} to register an ERC-8004 agent for Demos identity {demosPublicKey}. Timestamp: {timestamp}"
 * - signature: Signed by Demos wallet (ed25519 or other supported algorithm)
 * - demosPublicKey: The Demos identity's ed25519 public key
 *


 ... (clipped 184 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: 🏷️
Untrusted data stored: The agent identity add routine stores untrusted fields (proof, tokenUri, resolverUrl, and
txHash) into the database without sanitization/size limits and without performing the
on-chain/proof verification in the write path.

Referred Code
// SECTION Agent Identity Routines
static async applyAgentIdentityAdd(
    editOperation: any,
    gcrMainRepository: Repository<GCRMain>,
    simulate: boolean,
): Promise<GCRResult> {
    const payload = editOperation.data as AgentIdentityPayload

    // Validate required fields
    if (
        !payload.agentId ||
        !payload.evmAddress ||
        !payload.chain ||
        !payload.proof
    ) {
        return {
            success: false,
            message: "Invalid edit operation data: missing required fields (agentId, evmAddress, chain, proof)",
        }
    }



 ... (clipped 77 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 9, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Security
Verify with demosPublicKey

Fix a security vulnerability by using proof.demosPublicKey instead of sender as
the public key for signature verification.

src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts [174-179]

 const isValid = await ucrypto.verify({
     algorithm: algorithm as "ed25519" | "ml-dsa" | "falcon",
     message: new TextEncoder().encode(proof.message),
     signature: hexToUint8Array(signatureHex.replace(/^0x/i, "")),
-    publicKey: hexToUint8Array(sender.replace(/^0x/i, "")),
+    publicKey: hexToUint8Array(proof.demosPublicKey.replace(/^0x/i, "")),
 })
  • Apply / Chat
Suggestion importance[1-10]: 10

__

Why: This is a critical security fix; the signature verification was using the sender address instead of the proof.demosPublicKey from the proof object, which is the correct public key to use for verifying the signature.

High
Possible issue
Restore UD import

Restore the removed UDIdentityAssignPayload import, which is still in use and
will cause a compilation error.

src/libs/network/routines/transactions/handleIdentityRequest.ts [1-6]

 import {
     IdentityPayload,
     InferFromSignaturePayload,
     Web2CoreTargetIdentityPayload,
+    UDIdentityAssignPayload,
     AgentIdentityAssignPayload,
 } from "@kynesyslabs/demosdk/abstraction"

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies that the UDIdentityAssignPayload import was mistakenly removed, which would cause a compilation error as it is still used in the ud_identity_assign case.

High
Add missing break

Add a break statement to the agentremove case in the switch block to prevent
fall-through.

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts [1009-1014]

 case "agentremove":
     result = await this.applyAgentIdentityRemove(
         identityEdit,
         gcrMainRepository,
         simulate,
     )
+    break
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a missing break statement in a switch case, which would cause an unintended fall-through to the default case and lead to incorrect logic execution.

Medium
General
Add missing comma

Add a missing comma in the return type definition of getUserIdentitiesFromGCR to
fix a syntax error.

src/features/incentive/PointSystem.ts [42-56]

 private async getUserIdentitiesFromGCR(userId: string): Promise<{
     linkedWallets: string[]
     linkedSocials: { twitter?: string; github?: string; discord?: string }
     linkedUDDomains: {
         [network: string]: string[]
-    }
+    },
     linkedAgents: {
         [chain: string]: Array<{
             agentId: string
             evmAddress: string
             tokenUri?: string
             timestamp: number
         }>
     }
 }> {

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a syntax error in the TypeScript type definition where a comma is missing, which would prevent the code from compiling.

High
  • Update

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/features/incentive/PointSystem.ts (2)

15-25: Agent point constant is fine; ensure chain constraints are enforced consistently across layers.
Given current verification looks Base-Sepolia-specific, letting arbitrary chain strings flow into point keys (${chain}:${agentId}) can create inconsistent bookkeeping.


410-429: Avoid multi-line template literals in user-facing message fields (whitespace/newlines in API output).
These strings are now split across lines and will likely include formatting whitespace.

Example fix pattern
-                    message: `Points awarded for linking ${isDemosDomain ? ".demos" : "UD"
-                        } domain`,
+                    message: `Points awarded for linking ${isDemosDomain ? ".demos" : "UD"} domain`,

Also applies to: 1178-1188, 1260-1270

src/libs/network/routines/transactions/handleIdentityRequest.ts (1)

1-15: Fix import mismatch: AgentIdentityAssignPayload should come from local types, not SDK.

handleIdentityRequest.ts imports AgentIdentityAssignPayload from @kynesyslabs/demosdk/abstraction, but AgentIdentityManager.verifyPayload() expects the local type from @/model/entities/types/IdentityTypes. These types have different sources and may diverge. The code will compile but fail silently if the SDK type shape ever changes. Use the local type consistently throughout this flow.

🤖 Fix all issues with AI agents
In @src/features/incentive/PointSystem.ts:
- Around line 1284-1444: AgentIdentityManager.verifyPayload currently allows
caller-supplied chain values; update it to explicitly validate or normalize the
chain (e.g., require chain === "base.sepolia" or map known aliases to the
canonical "base.sepolia") and reject/throw on mismatch so verifyAgentOwnership
cannot be spoofed; then in PointSystem.awardAgentPoints validate/normalize the
incoming chain parameter (or call the new AgentIdentityManager validation
helper) before using it to read account.identities.agent?.[chain] and before
constructing agentKey so stored/looked-up keys always use the canonical chain
identifier.

In @src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts:
- Around line 213-296: verifyPayload currently logs payload.payload.chain but
always verifies against the Base-Sepolia registry; ensure the chain in the
payload is enforced or used to route verification. Update verifyPayload to read
payload.payload.chain and either (A) validate it equals the allowed chain(s)
(e.g., "base-sepolia") and return failure if not, or (B) pass the chain into
verifyAgentOwnership (and any on-chain checks) so the ownership check is
performed on the correct registry for that chain; also include the chain in
error/log messages. Modify verifyPayload, and if needed change
verifyAgentOwnership signature to accept a chain parameter and use it to select
the correct on-chain registry.
- Around line 25-98: The verifyAgentOwnership loop can hang on slow RPCs because
JsonRpcProvider has no timeout; in AgentIdentityManager.verifyAgentOwnership,
after creating the JsonRpcProvider(rpcUrl) instance set its underlying
connection timeout (provider._getConnection().timeout = 5000) before
constructing ethers.Contract or calling ownerOf, and apply this for every rpcUrl
in BASE_SEPOLIA_RPC_ENDPOINTS so each provider call times out (e.g., 5s) and the
loop can failover promptly.

In @src/libs/blockchain/gcr/gcr_routines/identityManager.ts:
- Around line 279-286: In verifyPqcPayload, the returned message uses a
multi-line template literal which embeds newlines/whitespace into the string;
update the message construction for the returned object (the message property)
to build a single-line string (e.g., by removing the line breaks inside the
template literal or concatenating parts) so the output does not contain
unintended newlines — keep logic that pluralizes "Signature proof(s)" and
includes JSON.stringify(payloads.map(p => p.algorithm)) unchanged.
🧹 Nitpick comments (7)
src/model/entities/types/IdentityTypes.ts (1)

120-139: StoredIdentities.agent shape is good; consider also defining a dedicated points breakdown type for agents.
It’ll help keep PointSystem consistent and avoid any/stringly-typed keys for agent scoring.

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts (2)

800-857: Removal path looks OK; consider cleaning up empty chain buckets.
After removal you may end up with identities.agent[chain] = []. Not required, but can keep stored JSON smaller over time.


1040-1070: Consider switching the agent “first connection” query to jsonb_extract_path for safer parameterization.
This avoids any ambiguity around gcr.identities->'agent'->:chain parameter typing and makes null-handling clearer.

Proposed query rewrite
             const result = await gcrMainRepository
                 .createQueryBuilder("gcr")
                 .where(
-                    "EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(gcr.identities->'agent'->:chain, '[]'::jsonb)) AS agent_id WHERE agent_id->>'agentId' = :agentId)",
-                    { chain: data.chain, agentId: data.agentId },
+                    "EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(jsonb_extract_path(gcr.identities, 'agent', :chain), '[]'::jsonb)) AS agent_id WHERE agent_id->>'agentId' = :agentId)",
+                    { chain: data.chain, agentId: data.agentId },
                 )
src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts (1)

112-199: Allowlist proof.signature.type before passing to ucrypto.verify.
Right now any string can be cast to "ed25519" | "ml-dsa" | "falcon", which can turn into confusing runtime failures.

Proposed hardening
             if (typeof proof.signature === "string") {
                 signatureHex = proof.signature
                 algorithm = "ed25519" // Default to ed25519
             } else {
                 signatureHex = proof.signature.data
                 algorithm = proof.signature.type
             }
+
+            if (!["ed25519", "ml-dsa", "falcon"].includes(algorithm)) {
+                return {
+                    success: false,
+                    message: `Unsupported signature algorithm: ${algorithm}`,
+                }
+            }
src/features/incentive/PointSystem.ts (2)

42-158: linkedAgents plumbing looks good; consider using a single “agent identities” accessor to avoid duplication.
You now have both IdentityManager.getAgentIdentities and AgentIdentityManager.getAgentIdentities; picking one reduces drift risk.


226-285: Good: addPointsToGCR supports agents; consider initializing udDomains + agents in the default breakdown shape.
Right now they’re conditionally created; eager init reduces null checks and makes the stored schema more consistent.

src/libs/blockchain/gcr/gcr_routines/identityManager.ts (1)

330-369: getAgentIdentities() is useful; consider tightening types + avoiding any.
Right now getIdentities(): Promise<any> and getAgentIdentities() return untyped structures, which makes it easy to drift from StoredIdentities['agent'].

TypeORM + Postgres: what’s the best practice for querying existence within JSONB arrays (jsonb_array_elements / jsonb_path_exists) with parameters?
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d048d5e and d9084ea.

📒 Files selected for processing (8)
  • src/features/incentive/PointSystem.ts
  • src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/IncentiveManager.ts
  • src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts
  • src/libs/blockchain/gcr/gcr_routines/identityManager.ts
  • src/libs/network/routines/transactions/handleIdentityRequest.ts
  • src/model/entities/GCRv2/GCR_Main.ts
  • src/model/entities/types/IdentityTypes.ts
🧰 Additional context used
🧬 Code graph analysis (4)
src/libs/blockchain/gcr/gcr_routines/IncentiveManager.ts (1)
tests/mocks/demosdk-types.ts (1)
  • RPCResponse (6-11)
src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts (3)
src/model/entities/types/IdentityTypes.ts (3)
  • DemosOwnershipProof (59-66)
  • AgentIdentityAssignPayload (104-109)
  • SavedAgentIdentity (79-88)
tests/mocks/demosdk-encryption.ts (2)
  • ucrypto (6-23)
  • hexToUint8Array (29-32)
src/libs/blockchain/gcr/gcr_routines/ensureGCRForUser.ts (1)
  • ensureGCRForUser (8-33)
src/features/incentive/PointSystem.ts (3)
src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts (1)
  • AgentIdentityManager (48-354)
src/model/entities/types/IdentityTypes.ts (1)
  • SavedAgentIdentity (79-88)
src/libs/blockchain/gcr/gcr_routines/ensureGCRForUser.ts (1)
  • ensureGCRForUser (8-33)
src/libs/network/routines/transactions/handleIdentityRequest.ts (2)
src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts (1)
  • AgentIdentityManager (48-354)
src/model/entities/types/IdentityTypes.ts (1)
  • AgentIdentityAssignPayload (104-109)
🔇 Additional comments (3)
src/model/entities/GCRv2/GCR_Main.ts (1)

37-37: LGTM! The agents field follows established patterns.

The optional agents field is well-structured and matches the pattern used by udDomains and web3Wallets. The optional flag ensures backward compatibility with existing records. Consuming code in awardAgentPoints, deductAgentPoints, and addPointsToGCR properly handles the optional nature with safe defaults and explicit initialization.

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts (1)

1002-1015: agentadd/agentremove wiring in apply() looks consistent.

src/libs/blockchain/gcr/gcr_routines/IncentiveManager.ts (1)

164-191: Agent hooks follow the established incentive pattern consistently.

Both awardAgentPoints() and deductAgentPoints() exist in PointSystem with matching signatures. Implementation includes proper agent verification, duplicate award prevention, and correct error handling—mirroring all existing linked/unlinked hooks (wallet, Twitter, GitHub, Discord, UD domains).

Comment on lines 1284 to 1444
/**
* Award points for linking an ERC-8004 agent
* @param userId The user's Demos address
* @param agentId The ERC-8004 agent token ID
* @param chain The chain where agent is registered (e.g., "base.sepolia")
* @param referralCode Optional referral code
* @returns RPCResponse
*/
async awardAgentPoints(
userId: string,
agentId: string,
chain: string,
referralCode?: string,
): Promise<RPCResponse> {
try {
// Create agentKey for point tracking: "chain:agentId"
const agentKey = `${chain}:${agentId}`

// Verify the agent is actually linked to this user
const account = await ensureGCRForUser(userId)
const agentIdentities = account.identities.agent?.[chain] || []
const hasAgent = agentIdentities.some(
(agent: SavedAgentIdentity) => agent.agentId === agentId,
)

if (!hasAgent) {
return {
result: 400,
response: {
pointsAwarded: 0,
totalPoints: account.points.totalPoints || 0,
message: "Error: Agent not linked to this user",
},
require_reply: false,
extra: {},
}
}

// Check if agent points already awarded
const agents = account.points.breakdown?.agents || {}
const agentAlreadyAwarded = agentKey in agents

if (agentAlreadyAwarded) {
const userPointsWithIdentities =
await this.getUserPointsInternal(userId)
return {
result: 200,
response: {
pointsAwarded: 0,
totalPoints: userPointsWithIdentities.totalPoints,
message: "Agent points already awarded",
},
require_reply: false,
extra: {},
}
}

// Award points by updating the GCR
await this.addPointsToGCR(
userId,
pointValues.LINK_AGENT,
"agents",
agentKey,
referralCode,
)

// Get updated points
const updatedPoints = await this.getUserPointsInternal(userId)

return {
result: 200,
response: {
pointsAwarded: pointValues.LINK_AGENT,
totalPoints: updatedPoints.totalPoints,
message: "Points awarded for linking ERC-8004 agent",
},
require_reply: false,
extra: {},
}
} catch (error) {
return {
result: 500,
response: "Error awarding agent points",
require_reply: false,
extra: {
error:
error instanceof Error ? error.message : String(error),
},
}
}
}

/**
* Deduct points for unlinking an ERC-8004 agent
* @param userId The user's Demos address
* @param agentId The ERC-8004 agent token ID
* @param chain The chain where agent was registered
* @returns RPCResponse
*/
async deductAgentPoints(
userId: string,
agentId: string,
chain: string,
): Promise<RPCResponse> {
try {
// Create agentKey for point tracking: "chain:agentId"
const agentKey = `${chain}:${agentId}`

// Check if user has points for this agent to deduct
const account = await ensureGCRForUser(userId)
const agents = account.points.breakdown?.agents || {}
const hasAgentPoints = agentKey in agents && agents[agentKey] > 0

if (!hasAgentPoints) {
const userPointsWithIdentities =
await this.getUserPointsInternal(userId)
return {
result: 200,
response: {
pointsDeducted: 0,
totalPoints: userPointsWithIdentities.totalPoints,
message: "No agent points to deduct",
},
require_reply: false,
extra: {},
}
}

// Deduct points by updating the GCR
await this.addPointsToGCR(
userId,
-pointValues.LINK_AGENT,
"agents",
agentKey,
)

// Get updated points
const updatedPoints = await this.getUserPointsInternal(userId)

return {
result: 200,
response: {
pointsDeducted: pointValues.LINK_AGENT,
totalPoints: updatedPoints.totalPoints,
message: "Points deducted for unlinking ERC-8004 agent",
},
require_reply: false,
extra: {},
}
} catch (error) {
return {
result: 500,
response: "Error deducting agent points",
require_reply: false,
extra: {
error:
error instanceof Error ? error.message : String(error),
},
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find relevant files and chain definitions
fd -t f "\.ts$" | head -20

Repository: kynesyslabs/node

Length of output: 966


🏁 Script executed:

# Search for chain identifier patterns and definitions
rg "base\.sepolia|chain.*identity|SUPPORTED_CHAINS" -t ts -B 2 -A 2

Repository: kynesyslabs/node

Length of output: 6686


🏁 Script executed:

# Look for SavedAgentIdentity type definition and how chain is used
rg "SavedAgentIdentity|identities\.agent" -t ts -B 3 -A 3

Repository: kynesyslabs/node

Length of output: 11263


🏁 Script executed:

# Check for chain validation or verification logic
rg "chain.*verify|verify.*chain|supported.*chain" -t ts -i

Repository: kynesyslabs/node

Length of output: 1862


🏁 Script executed:

# Look at the SUPPORTED CHAINS definition in identityManager.ts
rg "SUPPORTED_CHAINS|SUPPORTED CHAINS" -t ts -B 5 -A 10

Repository: kynesyslabs/node

Length of output: 1281


🏁 Script executed:

# Check agentIdentityManager for chain validation during agent registration
cat -n src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts | head -150

Repository: kynesyslabs/node

Length of output: 6525


🏁 Script executed:

# Search for AgentIdentityAssignPayload validation
rg "AgentIdentityAssignPayload" -t ts -B 3 -A 10

Repository: kynesyslabs/node

Length of output: 6828


🏁 Script executed:

# Look for any supported chains configuration/constants
fd -t f "chain" | xargs grep -l "SUPPORTED\|supported.*chain" 2>/dev/null | head -10

Repository: kynesyslabs/node

Length of output: 42


🏁 Script executed:

# Get the rest of the agentIdentityManager.ts file to see chain validation
cat -n src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts | sed -n '150,300p'

Repository: kynesyslabs/node

Length of output: 6308


🏁 Script executed:

# Check if there's explicit chain validation in verifyPayload
rg "chain" src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts -B 2 -A 2 | head -80

Repository: kynesyslabs/node

Length of output: 1799


🏁 Script executed:

# Look at the full flow to see where chain is validated
sed -n '150,400p' src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts

Repository: kynesyslabs/node

Length of output: 6780


Add explicit chain validation to prevent spoofing of agent's home chain.

The agent identity is always verified on-chain for Base Sepolia (hardcoded in AgentIdentityManager.verifyAgentOwnership), but the chain parameter from the payload is accepted without validation and used directly as a storage key in identities.agent[payload.chain]. This creates a mismatch: an agent verified on Base Sepolia could be stored under an arbitrary chain key (e.g., "ethereum.mainnet"), and awardAgentPoints would trust the caller-provided chain to retrieve it.

Add a chain validation step in AgentIdentityManager.verifyPayload to enforce that chain === "base.sepolia" (or map to a canonical chain identifier), then apply the same validation in awardAgentPoints before using the chain parameter for identity lookup.

🤖 Prompt for AI Agents
In @src/features/incentive/PointSystem.ts around lines 1284 - 1444,
AgentIdentityManager.verifyPayload currently allows caller-supplied chain
values; update it to explicitly validate or normalize the chain (e.g., require
chain === "base.sepolia" or map known aliases to the canonical "base.sepolia")
and reject/throw on mismatch so verifyAgentOwnership cannot be spoofed; then in
PointSystem.awardAgentPoints validate/normalize the incoming chain parameter (or
call the new AgentIdentityManager validation helper) before using it to read
account.identities.agent?.[chain] and before constructing agentKey so
stored/looked-up keys always use the canonical chain identifier.

Comment on lines 279 to 286
return {
success: true,
message: `Signature proof${
payloads.length > 1 ? "s" : ""
} verified. ${JSON.stringify(
payloads.map(p => p.algorithm),
)} identities assigned`,
message: `Signature proof${payloads.length > 1 ? "s" : ""
} verified. ${JSON.stringify(
payloads.map(p => p.algorithm),
)} identities assigned`,
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid template-literal newlines in verifyPqcPayload message (likely changes output).
Current formatting will embed whitespace/newlines into the returned message string.

Proposed fix
         return {
             success: true,
-            message: `Signature proof${payloads.length > 1 ? "s" : ""
-                } verified. ${JSON.stringify(
-                    payloads.map(p => p.algorithm),
-                )} identities assigned`,
+            message: `Signature proof${payloads.length > 1 ? "s" : ""} verified. ${JSON.stringify(
+                payloads.map(p => p.algorithm),
+            )} identities assigned`,
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
success: true,
message: `Signature proof${
payloads.length > 1 ? "s" : ""
} verified. ${JSON.stringify(
payloads.map(p => p.algorithm),
)} identities assigned`,
message: `Signature proof${payloads.length > 1 ? "s" : ""
} verified. ${JSON.stringify(
payloads.map(p => p.algorithm),
)} identities assigned`,
}
}
return {
success: true,
message: `Signature proof${payloads.length > 1 ? "s" : ""} verified. ${JSON.stringify(
payloads.map(p => p.algorithm),
)} identities assigned`,
}
🤖 Prompt for AI Agents
In @src/libs/blockchain/gcr/gcr_routines/identityManager.ts around lines 279 -
286, In verifyPqcPayload, the returned message uses a multi-line template
literal which embeds newlines/whitespace into the string; update the message
construction for the returned object (the message property) to build a
single-line string (e.g., by removing the line breaks inside the template
literal or concatenating parts) so the output does not contain unintended
newlines — keep logic that pluralizes "Signature proof(s)" and includes
JSON.stringify(payloads.map(p => p.algorithm)) unchanged.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 9, 2026

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @src/features/incentive/PointSystem.ts:
- Around line 1398-1410: The deductAgentPoints method builds agentKey from the
raw chain parameter without normalization, causing mismatched keys versus
awardAgentPoints; call AgentIdentityManager.validateChain(chain) (as done in
awardAgentPoints) to validate/normalize the chain before constructing agentKey
(`const agentKey = `${normalizedChain}:${agentId}``) and use that normalized
value for all subsequent lookups and updates (e.g., hasAgentPoints, agents
access, and any writes), ensuring behavior matches awardAgentPoints and
handling/propagating any validation errors as needed.

In @src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts:
- Around line 768-777: The savedAgent object in GCRIdentityRoutines.ts uses
Date.now() for timestamp, causing inconsistency with other identity types;
change the timestamp assignment in the SavedAgentIdentity construction
(savedAgent) to use payload.timestamp when present and only fall back to
Date.now() if payload.timestamp is missing (i.e., timestamp: payload.timestamp
?? Date.now()) so it matches XM/PQC/UD behavior.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d9084ea and f09f259.

📒 Files selected for processing (4)
  • src/features/incentive/PointSystem.ts
  • src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts
  • src/model/entities/types/IdentityTypes.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/features/incentive/PointSystem.ts (3)
src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts (1)
  • AgentIdentityManager (61-406)
src/model/entities/types/IdentityTypes.ts (1)
  • SavedAgentIdentity (67-76)
src/libs/blockchain/gcr/gcr_routines/ensureGCRForUser.ts (1)
  • ensureGCRForUser (8-33)
🔇 Additional comments (10)
src/model/entities/types/IdentityTypes.ts (2)

93-96: LGTM: Agent identity storage structure.

The agent identity storage structure is consistent with other identity types and properly supports multi-chain scenarios.


2-2: DemosOwnershipProof type is available in the SDK.

The DemosOwnershipProof type from @kynesyslabs/demosdk/abstraction version 2.8.3 is confirmed to be available and properly imported. The type is already being used in src/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts alongside other abstraction types, and the import pattern is consistent with other SDK imports throughout the codebase.

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts (5)

709-746: LGTM: Comprehensive validation.

The validation logic properly checks required fields, validates EVM address format, verifies chain support, and ensures proof structure integrity.


781-808: LGTM: Proper incentive integration.

The incentive integration follows established patterns, correctly checks for first connection to prevent duplicate awards, and properly delegates to IncentiveManager.


810-866: LGTM: Agent removal logic is sound.

The removal method properly validates inputs, verifies existence before removal, and correctly integrates with the incentive system for point deduction.


1063-1080: LGTM: First connection check is properly implemented.

The agent first connection check uses parameterized queries to prevent SQL injection and follows the same pattern as other identity types. The PostgreSQL-specific jsonb functions are consistent with the project's database choice.


1012-1025: LGTM: Switch case routing.

The agent identity routing is correctly integrated into the main apply switch statement and follows established conventions.

src/features/incentive/PointSystem.ts (3)

48-55: LGTM: Agent identity retrieval.

The linkedAgents retrieval follows established patterns for other identity types and properly groups agents by chain.

Also applies to: 74-74, 133-157


24-24: LGTM: Point system extensions.

The LINK_AGENT point value and agents type handling are properly integrated into the existing point system with appropriate initialization.

Also applies to: 229-229, 277-285


1284-1389: LGTM: Agent point award implementation.

The awardAgentPoints method properly validates chain, verifies agent ownership, prevents duplicate awards, and follows established patterns for point awarding.

Comment on lines +1398 to +1410
async deductAgentPoints(
userId: string,
agentId: string,
chain: string,
): Promise<RPCResponse> {
try {
// Create agentKey for point tracking: "chain:agentId"
const agentKey = `${chain}:${agentId}`

// Check if user has points for this agent to deduct
const account = await ensureGCRForUser(userId)
const agents = account.points.breakdown?.agents || {}
const hasAgentPoints = agentKey in agents && agents[agentKey] > 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Chain identifier not validated before use in deductAgentPoints.

Line 1405 constructs agentKey using the raw chain parameter without validation or normalization. This is inconsistent with awardAgentPoints (line 1300), which validates and normalizes the chain using AgentIdentityManager.validateChain. If the chain format differs between award and deduct operations, the keys won't match, potentially preventing proper point deduction.

Proposed fix
     async deductAgentPoints(
         userId: string,
         agentId: string,
         chain: string,
     ): Promise<RPCResponse> {
         try {
+            // Validate and normalize chain to canonical form
+            const canonicalChain = AgentIdentityManager.validateChain(chain)
+            if (!canonicalChain) {
+                return {
+                    result: 400,
+                    response: {
+                        pointsDeducted: 0,
+                        totalPoints: 0,
+                        message: `Invalid chain: ${chain}. Only base.sepolia is currently supported.`,
+                    },
+                    require_reply: false,
+                    extra: {},
+                }
+            }
+
             // Create agentKey for point tracking: "chain:agentId"
-            const agentKey = `${chain}:${agentId}`
+            const agentKey = `${canonicalChain}:${agentId}`
 
             // Check if user has points for this agent to deduct
             const account = await ensureGCRForUser(userId)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async deductAgentPoints(
userId: string,
agentId: string,
chain: string,
): Promise<RPCResponse> {
try {
// Create agentKey for point tracking: "chain:agentId"
const agentKey = `${chain}:${agentId}`
// Check if user has points for this agent to deduct
const account = await ensureGCRForUser(userId)
const agents = account.points.breakdown?.agents || {}
const hasAgentPoints = agentKey in agents && agents[agentKey] > 0
async deductAgentPoints(
userId: string,
agentId: string,
chain: string,
): Promise<RPCResponse> {
try {
// Validate and normalize chain to canonical form
const canonicalChain = AgentIdentityManager.validateChain(chain)
if (!canonicalChain) {
return {
result: 400,
response: {
pointsDeducted: 0,
totalPoints: 0,
message: `Invalid chain: ${chain}. Only base.sepolia is currently supported.`,
},
require_reply: false,
extra: {},
}
}
// Create agentKey for point tracking: "chain:agentId"
const agentKey = `${canonicalChain}:${agentId}`
// Check if user has points for this agent to deduct
const account = await ensureGCRForUser(userId)
const agents = account.points.breakdown?.agents || {}
const hasAgentPoints = agentKey in agents && agents[agentKey] > 0
🤖 Prompt for AI Agents
In @src/features/incentive/PointSystem.ts around lines 1398 - 1410, The
deductAgentPoints method builds agentKey from the raw chain parameter without
normalization, causing mismatched keys versus awardAgentPoints; call
AgentIdentityManager.validateChain(chain) (as done in awardAgentPoints) to
validate/normalize the chain before constructing agentKey (`const agentKey =
`${normalizedChain}:${agentId}``) and use that normalized value for all
subsequent lookups and updates (e.g., hasAgentPoints, agents access, and any
writes), ensuring behavior matches awardAgentPoints and handling/propagating any
validation errors as needed.

Comment on lines +768 to +777
const savedAgent: SavedAgentIdentity = {
agentId: payload.agentId,
evmAddress: payload.evmAddress.toLowerCase(),
chain: canonicalChain,
txHash: payload.txHash,
tokenUri: payload.tokenUri,
proof: payload.proof,
timestamp: Date.now(),
resolverUrl: payload.resolverUrl,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Inconsistent timestamp handling.

Line 775 uses Date.now() to set the timestamp, while other identity types (XM, PQC, UD) preserve the timestamp from the payload. This inconsistency could cause issues with timestamp tracking and auditing.

Proposed fix
 const savedAgent: SavedAgentIdentity = {
     agentId: payload.agentId,
     evmAddress: payload.evmAddress.toLowerCase(),
     chain: canonicalChain,
     txHash: payload.txHash,
     tokenUri: payload.tokenUri,
     proof: payload.proof,
-    timestamp: Date.now(),
+    timestamp: payload.timestamp || Date.now(),
     resolverUrl: payload.resolverUrl,
 }

This preserves the payload timestamp when available (similar to other identity types) while falling back to Date.now() if not provided.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const savedAgent: SavedAgentIdentity = {
agentId: payload.agentId,
evmAddress: payload.evmAddress.toLowerCase(),
chain: canonicalChain,
txHash: payload.txHash,
tokenUri: payload.tokenUri,
proof: payload.proof,
timestamp: Date.now(),
resolverUrl: payload.resolverUrl,
}
const savedAgent: SavedAgentIdentity = {
agentId: payload.agentId,
evmAddress: payload.evmAddress.toLowerCase(),
chain: canonicalChain,
txHash: payload.txHash,
tokenUri: payload.tokenUri,
proof: payload.proof,
timestamp: payload.timestamp || Date.now(),
resolverUrl: payload.resolverUrl,
}
🤖 Prompt for AI Agents
In @src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts around lines 768
- 777, The savedAgent object in GCRIdentityRoutines.ts uses Date.now() for
timestamp, causing inconsistency with other identity types; change the timestamp
assignment in the SavedAgentIdentity construction (savedAgent) to use
payload.timestamp when present and only fall back to Date.now() if
payload.timestamp is missing (i.e., timestamp: payload.timestamp ?? Date.now())
so it matches XM/PQC/UD behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants