-
Notifications
You must be signed in to change notification settings - Fork 2
Added ERC-8004 agent identity support #573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: testnet
Are you sure you want to change the base?
Conversation
WalkthroughAdds 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
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. Comment |
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||||
There was a problem hiding this 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 arbitrarychainstrings flow into point keys (${chain}:${agentId}) can create inconsistent bookkeeping.
410-429: Avoid multi-line template literals in user-facingmessagefields (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:AgentIdentityAssignPayloadshould come from local types, not SDK.
handleIdentityRequest.tsimportsAgentIdentityAssignPayloadfrom@kynesyslabs/demosdk/abstraction, butAgentIdentityManager.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.agentshape is good; consider also defining a dedicated points breakdown type foragents.
It’ll help keepPointSystemconsistent and avoidany/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 withidentities.agent[chain] = []. Not required, but can keep stored JSON smaller over time.
1040-1070: Consider switching the agent “first connection” query tojsonb_extract_pathfor safer parameterization.
This avoids any ambiguity aroundgcr.identities->'agent'->:chainparameter 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: Allowlistproof.signature.typebefore passing toucrypto.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:linkedAgentsplumbing looks good; consider using a single “agent identities” accessor to avoid duplication.
You now have bothIdentityManager.getAgentIdentitiesandAgentIdentityManager.getAgentIdentities; picking one reduces drift risk.
226-285: Good:addPointsToGCRsupportsagents; consider initializingudDomains+agentsin 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 + avoidingany.
Right nowgetIdentities(): Promise<any>andgetAgentIdentities()return untyped structures, which makes it easy to drift fromStoredIdentities['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.
📒 Files selected for processing (8)
src/features/incentive/PointSystem.tssrc/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.tssrc/libs/blockchain/gcr/gcr_routines/IncentiveManager.tssrc/libs/blockchain/gcr/gcr_routines/agentIdentityManager.tssrc/libs/blockchain/gcr/gcr_routines/identityManager.tssrc/libs/network/routines/transactions/handleIdentityRequest.tssrc/model/entities/GCRv2/GCR_Main.tssrc/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
agentsfield is well-structured and matches the pattern used byudDomainsandweb3Wallets. The optional flag ensures backward compatibility with existing records. Consuming code inawardAgentPoints,deductAgentPoints, andaddPointsToGCRproperly handles the optional nature with safe defaults and explicit initialization.src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts (1)
1002-1015:agentadd/agentremovewiring inapply()looks consistent.src/libs/blockchain/gcr/gcr_routines/IncentiveManager.ts (1)
164-191: Agent hooks follow the established incentive pattern consistently.Both
awardAgentPoints()anddeductAgentPoints()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).
| /** | ||
| * 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), | ||
| }, | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, find relevant files and chain definitions
fd -t f "\.ts$" | head -20Repository: 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 2Repository: 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 3Repository: kynesyslabs/node
Length of output: 11263
🏁 Script executed:
# Check for chain validation or verification logic
rg "chain.*verify|verify.*chain|supported.*chain" -t ts -iRepository: 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 10Repository: 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 -150Repository: kynesyslabs/node
Length of output: 6525
🏁 Script executed:
# Search for AgentIdentityAssignPayload validation
rg "AgentIdentityAssignPayload" -t ts -B 3 -A 10Repository: 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 -10Repository: 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 -80Repository: 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.tsRepository: 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.
| 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`, | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
|
There was a problem hiding this 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.
📒 Files selected for processing (4)
src/features/incentive/PointSystem.tssrc/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.tssrc/libs/blockchain/gcr/gcr_routines/agentIdentityManager.tssrc/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
DemosOwnershipProoftype from@kynesyslabs/demosdk/abstractionversion 2.8.3 is confirmed to be available and properly imported. The type is already being used insrc/libs/blockchain/gcr/gcr_routines/agentIdentityManager.tsalongside 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.
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| 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, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.



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
File Walkthrough
PointSystem.ts
Add agent identity point system integrationsrc/features/incentive/PointSystem.ts
LINK_AGENTpoint value (2 points) to point values configurationgetUserIdentitiesFromGCRreturn type to includelinkedAgentsstructure with agent details
AgentIdentityManagerto fetch agent identities for userslinkedAgentsmappingby chain
getUserPointsInternalto includelinkedAgentsin returneduser points
addPointsToGCRto support "agents" type for point breakdowntracking
awardAgentPointsmethod to award points when agent islinked with duplicate prevention
deductAgentPointsmethod to deduct points when agent isunlinked
GCRIdentityRoutines.ts
Implement agent identity GCR operationssrc/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts
applyAgentIdentityAddmethod to handle agent identity linkingwith validation
applyAgentIdentityRemovemethod to handle agent identityunlinking
isFirstConnectionmethod to support "agent" type withdatabase query for duplicate detection
applyIdentityEditswitch statement to route "agentadd" and"agentremove" operations
IncentiveManagerhooks for agent linking/unlinking pointawards
identity operations
IncentiveManager.ts
Add agent identity incentive hookssrc/libs/blockchain/gcr/gcr_routines/IncentiveManager.ts
agentLinkedhook method to award points after agent identitylinking
agentUnlinkedhook method to deduct points after agent identityunlinking
PointSystemfor point calculation and storageagentIdentityManager.ts
Create agent identity manager with verificationsrc/libs/blockchain/gcr/gcr_routines/agentIdentityManager.ts
storage
verifyAgentOwnershipto check on-chain NFT ownership viaBase Sepolia RPC with failover support
verifyOwnershipProofto validate Demos signature-basedownership proof
verifyPayloadto orchestrate full verification flowincluding proof and on-chain checks
getAgentIdentitiesto retrieve agent identities for aDemos address with optional chain filtering
getIdentitieshelper for generic identity retrievalgetConfigto expose agent registry configurationwith failover
identityManager.ts
Extend identity manager for agent supportsrc/libs/blockchain/gcr/gcr_routines/identityManager.ts
getIdentitiesmethod to support "agent" identity typegetAgentIdentitiesmethod to retrieve agent identities withoptional chain filtering
handleIdentityRequest.ts
Integrate agent identity request handlingsrc/libs/network/routines/transactions/handleIdentityRequest.ts
AgentIdentityManagerandAgentIdentityAssignPayloadhandling pipeline
GCR_Main.ts
Add agents field to point breakdownsrc/model/entities/GCRv2/GCR_Main.ts
agentsfield to points breakdown structure for trackingagent-specific point allocations
IdentityTypes.ts
Define agent identity types and interfacessrc/model/entities/types/IdentityTypes.ts
DemosOwnershipProofinterface for signature-based ownershipverification
SavedAgentIdentityinterface to represent stored ERC-8004 agentidentities with metadata
AgentIdentityPayloadinterface for agent identity assignmentpayload
AgentIdentityAssignPayloadinterface for transaction payloadwrapper
AgentIdentityRemovePayloadinterface for agent identity removalpayload
StoredIdentitiestype to includeagentfield mapping chainsto agent identity arrays
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.