Skip to content

feat(revm): initial revive support#7

Open
hassnian wants to merge 9 commits into
mainfrom
feat/add-revive-support
Open

feat(revm): initial revive support#7
hassnian wants to merge 9 commits into
mainfrom
feat/add-revive-support

Conversation

@hassnian
Copy link
Copy Markdown

@hassnian hassnian commented Mar 30, 2026

Summary by CodeRabbit

  • New Features

    • Paseo testnet support and Revive EVM integration for ERC‑721 collections: register, mint, transfer, burn
    • Collection kind classification added (UNIQUES, NFTS, ERC721, ERC1155)
    • On‑chain metadata ingestion and contract‑URI updates for Revive collections
  • Updates

    • Environment/configuration: new runtime toggles, Paseo defaults, and revised starting‑block logic
    • Database migration adds non‑nullable collection type column and index; system asset defaults updated (Paseo/PAS)
    • Processor can conditionally enable Revive log ingestion
  • Tests

    • New unit tests covering Revive helpers, handlers, metadata flows, and attribute sanitization
  • Chores

    • Added EVM client dependency and indexer manifest/configuration

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Add a Revive EVM contract indexing subsystem (ABIs, viem client, decoders, handlers), collectionKind tracking and migration, Paseo chain support and environment flags, processor subscriptions for EVM logs, mapping wiring/getter routing changes, tests, a SubSquid manifest, and viem dependency.

Changes

Cohort / File(s) Summary
Database & GraphQL
db/migrations/1774691683926-Data.js, schema.graphql
Add collection_type column migration (populate/index/NOT NULL) and GraphQL enum CollectionKind with collectionType: CollectionKind!.
Manifest & deps
snack.yaml, package.json
Add SubSquid snack.yaml manifest and add viem dependency.
Environment
src/environment.ts
Add paseo chain, GETTER_CHAIN, SS58_PREFIX, Revive flags/addresses/RPC/start blocks, NODE_URL default, updated STARTING_BLOCK computation and prod detection.
Processor & processable
src/processor.ts, src/processable.ts
Register conditional Revive EVM subscriptions (registry + topics), import Revive topics, add Revive.contractEmitted processable, and source substrate start block from environment.
Revive ABIs & client
src/mappings/revive/abi/erc721.ts, src/mappings/revive/abi/registry.ts, src/mappings/revive/client.ts
Add ERC‑721 and registry ABI modules and a viem-backed RPC client with read helpers for registry/collection/owner/URIs.
Revive helpers & decoders
src/mappings/revive/helpers.ts
Add payload normalization, EVM address normalization, id utilities, mint/burn checks, topic extraction, base-call builder, and ABI decoders for registry/transfer/contractURI events.
Revive handlers
src/mappings/revive/index.ts, .../createCollection.ts, mint.ts, transfer.ts, burn.ts, updateCollectionMetadata.ts
Add dispatcher and handlers for collection create, metadata update, mint, transfer, and burn with store persistence and event emission logic.
Mapping wiring & getters
src/mappings/index.ts, src/mappings/.../getters/index.ts (uniques, nfts, assets)
Use GETTER_CHAIN for dynamic getter resolution, add revive handler wiring, add assetsForced guard and adjust asset forcing condition.
CollectionKind assignment
src/mappings/uniques/create.ts, src/mappings/uniques/forceCreate.ts, src/mappings/nfts/create.ts, src/mappings/nfts/forceCreate.ts
Set collectionType to CollectionKind.UNIQUES or CollectionKind.NFTS on create and force-create paths.
Helpers & codec
src/mappings/utils/helper.ts
Switch SS58 codec source to SS58_PREFIX and add sanitizeText(value) to strip NUL chars.
Tests
tests/index.test.ts, tests/revive.test.ts, tests/reviveHandlers.test.ts, tests/reviveCollectionMetadata.test.ts, tests/nftSetAttribute.test.ts
Add sanitizeText test and comprehensive Revive unit tests for helpers, handlers, metadata updates, and attribute sanitization.

Sequence Diagram(s)

sequenceDiagram
    participant Processor
    participant ReviveDispatcher as Revive Dispatcher
    participant EVMClient as Revive EVM Client
    participant CollectionHandler as Collection Handler
    participant MintHandler as Mint Handler
    participant TransferHandler as Transfer Handler
    participant BurnHandler as Burn Handler
    participant Store as Database Store

    Processor->>ReviveDispatcher: Revive.ContractEmitted (topic0, data)
    ReviveDispatcher->>ReviveDispatcher: inspect topic0 / decode
    alt CollectionRegistered
        ReviveDispatcher->>CollectionHandler: handleCollectionCreate(ctx)
        CollectionHandler->>EVMClient: readRegistryCollection(index, block)
        EVMClient-->>CollectionHandler: collection data
        CollectionHandler->>EVMClient: readContractUri(addr, block)
        EVMClient-->>CollectionHandler: uri
        CollectionHandler->>Store: save CollectionEntity
    else ERC721 Transfer
        ReviveDispatcher->>ReviveDispatcher: classify transfer (mint/burn/transfer)
        alt Mint
            ReviveDispatcher->>MintHandler: handleTokenMint(ctx, transfer)
            MintHandler->>EVMClient: readTokenUri(addr, tokenId, block)
            EVMClient-->>MintHandler: token uri
            MintHandler->>Store: save NFTEntity + update CollectionEntity
        else Burn
            ReviveDispatcher->>BurnHandler: handleTokenBurn(ctx, transfer)
            BurnHandler->>Store: mark NFT burned + update collection
        else Transfer
            ReviveDispatcher->>TransferHandler: handleTokenTransfer(ctx, transfer)
            TransferHandler->>Store: update NFT owner + recalc distribution
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • vikiival

Poem

🐰 I hopped to Paseo where new logs gleam bright,

ABIs and handlers working through the night.
Collections bloom, tokens leap and fly,
Mints, burns, transfers — I twitch my whiskers with delight.
Indexes hum, the store is snug and tight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.51% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(revm): initial revive support' clearly and accurately summarizes the main change—adding initial support for Revive (an EVM indexing feature). The title is specific, concise, and directly reflects the substantial additions throughout the codebase for Revive mapping handlers, ABIs, client utilities, and environment configuration.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-revive-support

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.

Copy link
Copy Markdown

@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: 7

Caution

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

⚠️ Outside diff range comments (1)
src/processor.ts (1)

83-87: ⚠️ Potential issue | 🟡 Minor

Duplicate event subscriptions detected.

Lines 83-84 and 86-87 register the same events (NonFungible.clearCollectionMetadata and NonFungible.setCollectionMetadata) twice. This appears to be a pre-existing issue but may cause events to be processed multiple times.

🐛 Proposed fix to remove duplicates
 processor.addEvent({ name: [NonFungible.clearCollectionMetadata], call: true, extrinsic: true }) 
 processor.addEvent({ name: [NonFungible.setCollectionMetadata], call: true, extrinsic: true }) 
 processor.addEvent({ name: [NonFungible.setCollectionMaxSupply], call: true, extrinsic: true }) 
-processor.addEvent({ name: [NonFungible.clearCollectionMetadata], call: true, extrinsic: true }) 
-processor.addEvent({ name: [NonFungible.setCollectionMetadata], call: true, extrinsic: true }) 
 processor.addEvent({ name: [NonFungible.destroyCollection], call: true, extrinsic: true }) 
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/processor.ts` around lines 83 - 87, The processor is registering
duplicate event subscriptions: remove the repeated calls to processor.addEvent
for NonFungible.clearCollectionMetadata and NonFungible.setCollectionMetadata so
each event is only registered once; update the event registration block that
calls processor.addEvent (the calls with NonFungible.clearCollectionMetadata and
NonFungible.setCollectionMetadata) to leave a single entry per event and keep
other events like NonFungible.setCollectionMaxSupply unchanged.
🧹 Nitpick comments (6)
tests/revive.test.ts (1)

23-28: Consider adding edge case tests for error handling.

The helper functions include validation (e.g., getReviveLogPayload throws on invalid payloads). Consider adding tests for:

  • Invalid/malformed event payloads
  • Missing or empty topics array
  • Non-string contract addresses

This would ensure error paths are exercised and provide regression protection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/revive.test.ts` around lines 23 - 28, Add unit tests in revive.test.ts
covering error paths for the helper functions: call getReviveLogPayload with
malformed/invalid payloads and assert it throws, supply an event object with
missing or empty topics to functions that expect topics and assert they throw or
return the expected error, and pass non-string contract addresses to
isMintTransfer/isBurnTransfer (or any address-validated helper) and assert they
reject/throw; target the functions getReviveLogPayload, isMintTransfer, and
isBurnTransfer when writing these tests so error handling and input validation
are exercised.
snack.yaml (1)

20-25: Potential confusion with STARTING_BLOCK env var.

The STARTING_BLOCK environment variable (5650083) appears to map to _NFT_STARTING_BLOCK in src/environment.ts, while the actual STARTING_BLOCK constant is computed as Math.min(PALLET_STARTING_BLOCK, REVIVE_START_BLOCK) when Revive is enabled. Since REVIVE_START_BLOCK (5649754) is earlier than the configured STARTING_BLOCK (5650083), the processor will actually start from block 5649754.

Consider either:

  1. Renaming the env var to NFT_STARTING_BLOCK for clarity, or
  2. Adding a comment explaining the relationship between these values
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@snack.yaml` around lines 20 - 25, The configured STARTING_BLOCK env var
(5650083) is mapped to _NFT_STARTING_BLOCK in src/environment.ts but the
effective STARTING_BLOCK used when revive is enabled is computed as
Math.min(PALLET_STARTING_BLOCK, REVIVE_START_BLOCK), which causes the processor
to actually start at REVIVE_START_BLOCK (5649754); update the code or docs to
avoid confusion by either renaming the env var to NFT_STARTING_BLOCK (and
updating references to _NFT_STARTING_BLOCK) or add a clear comment beside the
STARTING_BLOCK/_NFT_STARTING_BLOCK, PALLET_STARTING_BLOCK, and
REVIVE_START_BLOCK declarations explaining that the runtime STARTING_BLOCK is
Math.min(PALLET_STARTING_BLOCK, REVIVE_START_BLOCK) when REVIVE_ENABLED is true
so users know which value wins.
src/mappings/revive/index.ts (1)

30-31: Consider adding debug logging for unhandled topics.

The silent return on unmatched topics could make debugging difficult if unexpected events are delivered to this handler. A debug log would help identify configuration issues or new event types.

🔧 Optional: Add debug logging
+import { logger } from '@kodadot1/metasquid/logger'
+
 // ... in handleReviveEvent
     default:
+      logger.debug(`Unhandled Revive topic: ${topic0Of(context.event.args)}`)
       return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mappings/revive/index.ts` around lines 30 - 31, The switch's default
branch currently does a silent "return" for unmatched topics; update that branch
in src/mappings/revive/index.ts to emit a debug log (use the module's existing
logger variable, e.g., logger or processLogger, or fallback to console.debug)
that records the topic name and a short payload summary so unexpected events are
visible during debugging; locate the switch over topic (the default: return
case) and replace the silent return with a logger.debug/info call including the
topic and relevant metadata (and then return).
src/mappings/revive/client.ts (2)

38-57: Minor: Redundant type assertion can be simplified.

The type assertion on lines 44-49 is unnecessary since TypeScript already narrows the type in the else branch. Consider simplifying:

♻️ Suggested simplification
 function asRegistryCollection(result: RegistryCollectionResult) {
   if (Array.isArray(result)) {
     const [collectionAddress, deployer, name, createdAt] = result
     return { collectionAddress, deployer, name, createdAt }
   }

-  const value = result as {
-    collectionAddress: Address
-    deployer: Address
-    name: string
-    createdAt: bigint
-  }
-
   return {
-    collectionAddress: value.collectionAddress,
-    deployer: value.deployer,
-    name: value.name,
-    createdAt: value.createdAt,
+    collectionAddress: result.collectionAddress,
+    deployer: result.deployer,
+    name: result.name,
+    createdAt: result.createdAt,
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mappings/revive/client.ts` around lines 38 - 57, The asRegistryCollection
function uses an unnecessary type assertion for the else branch; remove the
redundant "as { collectionAddress: Address ... }" cast and directly access
properties on result (which TypeScript has already narrowed to
RegistryCollectionResult in the non-array branch). Update the else/return path
in asRegistryCollection to read result.collectionAddress, result.deployer,
result.name, and result.createdAt instead of creating the intermediate asserted
value.

78-122: Consider logging RPC errors for debugging.

The empty catch blocks silently swallow all errors, which could hide RPC connectivity issues or unexpected failures. Consider adding debug-level logging:

♻️ Suggested improvement
+import { warn } from '../utils/logger'
+
 export async function readCollectionOwner(address: string, blockNumber?: bigint): Promise<string> {
   try {
     const owner = (await getClient().readContract({
       address: address as Address,
       abi: erc721Abi,
       functionName: 'owner',
       blockNumber,
     })) as Hex

     return normalizeEvmAddress(owner)
-  } catch {
+  } catch (error) {
+    // owner() is optional for ERC-721, return empty on failure
     return ''
   }
 }

This helps distinguish between "contract doesn't implement this method" vs "RPC is down" scenarios during debugging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mappings/revive/client.ts` around lines 78 - 122, The three functions
readCollectionOwner, readContractUri and readTokenUri currently swallow all
errors in their catch blocks; update each catch to log the caught error (e.g.,
catch (err) { logger.debug?.('readCollectionOwner error', { address, tokenId?,
err }) } or fallback to console.debug) including function name, address, tokenId
when relevant, and the raw error/stack before returning '' so RPC/connectivity
vs missing-method cases can be distinguished; use the existing logging utility
if available (or console.debug) and include blockNumber in the log when present.
src/mappings/revive/createCollection.ts (1)

27-50: Consider handling re-registration of existing collections.

Using getOrCreate followed by unconditionally overwriting all fields (including nftCount, supply, ownerCount, volume) will reset these counters if a collection is re-registered. If re-registration is not expected, consider adding a check like in mint.ts:

const existing = await getOptional<CE>(context.store, CE, id)
if (existing) {
  warn(OPERATION, `Collection ${id} already exists`)
  return
}

If re-registration is valid, this is fine as-is, but worth documenting the intent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mappings/revive/createCollection.ts` around lines 27 - 50, The current
use of getOrCreate followed by unconditionally setting every field on final
(fields like nftCount, supply, ownerCount, volume) will reset counters on
re-registration; update createCollection logic to first check for an existing
collection via getOptional<CE>(context.store, CE, id) and, if it exists, log a
warning (use the same OPERATION warning pattern as in mint.ts) and return early
to avoid overwriting existing counters, or alternatively, if re-registration
must update some fields, merge values by preserving existing numeric counters
(nftCount, supply, ownerCount, volume) from the existing record instead of
overwriting them when assigning to final; locate the logic around getOrCreate
and final to implement the chosen approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@package.json`:
- Around line 33-35: The package.json currently lists "engines.node" as ">=16"
while the dependency "viem" is pinned to "^2.47.6" which requires Node.js >=18;
update package.json to either raise "engines.node" to ">=18" (preferred) or
change the "viem" dependency to a version compatible with Node 16, and update
any README/CI configs accordingly; ensure you modify the "engines.node" field
and/or the "viem" entry in dependencies to keep versions consistent.

In `@src/mappings/index.ts`:
- Around line 228-232: The current dual-bound check using STARTING_BLOCK and
_NFT_STARTING_BLOCK can skip forceAssets on the very first batch; update the
condition in the block that computes firstBlock and lastBlockInBatch to only
check STARTING_BLOCK <= firstBlock (and assetsForced === false) before calling
await forceAssets(ctx), leaving assetsForced logic intact to ensure it runs only
once; keep references to firstBlock, lastBlockInBatch, STARTING_BLOCK,
_NFT_STARTING_BLOCK, forceAssets, and assetsForced so the change is localized
and obvious.

In `@src/mappings/revive/burn.ts`:
- Around line 21-25: The burn handlers are passing entity.currentOwner as the
third (newOwner) argument to calculateCollectionOwnerCountAndDistribution, which
is wrong for burns; update the call in the burn handler
(calculateCollectionOwnerCountAndDistribution(...)) to either omit the third
parameter entirely or shift entity.currentOwner into the fourth parameter named
originalOwner so that newOwner is not set for burn operations; locate and fix
the same issue across the three burn handlers (nfts, uniques, revive) to ensure
calculateCollectionOwnerCountAndDistribution receives no newOwner for burns and
the owner being removed is passed as originalOwner when needed.

In `@src/mappings/revive/helpers.ts`:
- Around line 99-103: The guard in getReviveLogPayload is too weak: it only
checks Array.isArray(payload.topics) but allows an empty array which later
breaks when cast to [Hex, ...Hex[]]; update the validation to ensure
payload.topics is a non-empty array (e.g., check payload.topics.length > 0) and
optionally validate payload.topics[0] is a string/hex before returning the
ReviveLogPayload to prevent downstream decode failures.
- Around line 54-59: The toReviveBaseCall function uses Date.now() as a
nondeterministic fallback for timestamp; replace that with a deterministic
fallback (e.g., new Date(0)) or explicitly throw when context.block.timestamp is
missing. Update the return in toReviveBaseCall to construct timestamp from
context.block.timestamp when present and otherwise use a fixed value (new
Date(0)) or throw an error to avoid nondeterministic persisted values; ensure
you modify the BaseCall creation in toReviveBaseCall accordingly and adjust any
related tests/consumers.

In `@src/mappings/revive/transfer.ts`:
- Around line 20-22: The transfer handler in revive mapping fails to clear stale
listing prices; when updating ownership in the transfer logic (where you set
entity.currentOwner = transfer.to and entity.updatedAt = ...), also set
entity.price = null to mirror the uniques handler behavior and prevent old
prices from persisting; update the transfer function in
src/mappings/revive/transfer.ts to clear entity.price after assigning the new
owner.
- Around line 37-44: The revive transfer handler currently calls createEvent
with entity.currentOwner (the new owner), causing inconsistent semantics; change
the argument passed as the owner to the previous owner (oldOwner) to match
uniques and nfts handlers. Locate the createEvent invocation that uses
Interaction.SEND and toReviveBaseCall and replace the entity.currentOwner
parameter with the oldOwner variable (the sender from the transfer context) so
revive events record the prior owner like the other transfer handlers.

---

Outside diff comments:
In `@src/processor.ts`:
- Around line 83-87: The processor is registering duplicate event subscriptions:
remove the repeated calls to processor.addEvent for
NonFungible.clearCollectionMetadata and NonFungible.setCollectionMetadata so
each event is only registered once; update the event registration block that
calls processor.addEvent (the calls with NonFungible.clearCollectionMetadata and
NonFungible.setCollectionMetadata) to leave a single entry per event and keep
other events like NonFungible.setCollectionMaxSupply unchanged.

---

Nitpick comments:
In `@snack.yaml`:
- Around line 20-25: The configured STARTING_BLOCK env var (5650083) is mapped
to _NFT_STARTING_BLOCK in src/environment.ts but the effective STARTING_BLOCK
used when revive is enabled is computed as Math.min(PALLET_STARTING_BLOCK,
REVIVE_START_BLOCK), which causes the processor to actually start at
REVIVE_START_BLOCK (5649754); update the code or docs to avoid confusion by
either renaming the env var to NFT_STARTING_BLOCK (and updating references to
_NFT_STARTING_BLOCK) or add a clear comment beside the
STARTING_BLOCK/_NFT_STARTING_BLOCK, PALLET_STARTING_BLOCK, and
REVIVE_START_BLOCK declarations explaining that the runtime STARTING_BLOCK is
Math.min(PALLET_STARTING_BLOCK, REVIVE_START_BLOCK) when REVIVE_ENABLED is true
so users know which value wins.

In `@src/mappings/revive/client.ts`:
- Around line 38-57: The asRegistryCollection function uses an unnecessary type
assertion for the else branch; remove the redundant "as { collectionAddress:
Address ... }" cast and directly access properties on result (which TypeScript
has already narrowed to RegistryCollectionResult in the non-array branch).
Update the else/return path in asRegistryCollection to read
result.collectionAddress, result.deployer, result.name, and result.createdAt
instead of creating the intermediate asserted value.
- Around line 78-122: The three functions readCollectionOwner, readContractUri
and readTokenUri currently swallow all errors in their catch blocks; update each
catch to log the caught error (e.g., catch (err) {
logger.debug?.('readCollectionOwner error', { address, tokenId?, err }) } or
fallback to console.debug) including function name, address, tokenId when
relevant, and the raw error/stack before returning '' so RPC/connectivity vs
missing-method cases can be distinguished; use the existing logging utility if
available (or console.debug) and include blockNumber in the log when present.

In `@src/mappings/revive/createCollection.ts`:
- Around line 27-50: The current use of getOrCreate followed by unconditionally
setting every field on final (fields like nftCount, supply, ownerCount, volume)
will reset counters on re-registration; update createCollection logic to first
check for an existing collection via getOptional<CE>(context.store, CE, id) and,
if it exists, log a warning (use the same OPERATION warning pattern as in
mint.ts) and return early to avoid overwriting existing counters, or
alternatively, if re-registration must update some fields, merge values by
preserving existing numeric counters (nftCount, supply, ownerCount, volume) from
the existing record instead of overwriting them when assigning to final; locate
the logic around getOrCreate and final to implement the chosen approach.

In `@src/mappings/revive/index.ts`:
- Around line 30-31: The switch's default branch currently does a silent
"return" for unmatched topics; update that branch in
src/mappings/revive/index.ts to emit a debug log (use the module's existing
logger variable, e.g., logger or processLogger, or fallback to console.debug)
that records the topic name and a short payload summary so unexpected events are
visible during debugging; locate the switch over topic (the default: return
case) and replace the silent return with a logger.debug/info call including the
topic and relevant metadata (and then return).

In `@tests/revive.test.ts`:
- Around line 23-28: Add unit tests in revive.test.ts covering error paths for
the helper functions: call getReviveLogPayload with malformed/invalid payloads
and assert it throws, supply an event object with missing or empty topics to
functions that expect topics and assert they throw or return the expected error,
and pass non-string contract addresses to isMintTransfer/isBurnTransfer (or any
address-validated helper) and assert they reject/throw; target the functions
getReviveLogPayload, isMintTransfer, and isBurnTransfer when writing these tests
so error handling and input validation are exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78e5a21d-0a17-4f7a-9708-84eea9fd8445

📥 Commits

Reviewing files that changed from the base of the PR and between 421c0ce and 8cc00ce.

⛔ Files ignored due to path filters (4)
  • package-lock.json is excluded by !**/package-lock.json
  • src/model/generated/_collectionKind.ts is excluded by !**/generated/**
  • src/model/generated/collectionEntity.model.ts is excluded by !**/generated/**
  • src/model/generated/index.ts is excluded by !**/generated/**
📒 Files selected for processing (28)
  • db/migrations/1774691683926-Data.js
  • package.json
  • schema.graphql
  • snack.yaml
  • src/environment.ts
  • src/mappings/assets/forceToken.ts
  • src/mappings/assets/getters/index.ts
  • src/mappings/index.ts
  • src/mappings/nfts/create.ts
  • src/mappings/nfts/forceCreate.ts
  • src/mappings/nfts/getters/index.ts
  • src/mappings/revive/abi/erc721.ts
  • src/mappings/revive/abi/registry.ts
  • src/mappings/revive/burn.ts
  • src/mappings/revive/client.ts
  • src/mappings/revive/createCollection.ts
  • src/mappings/revive/helpers.ts
  • src/mappings/revive/index.ts
  • src/mappings/revive/mint.ts
  • src/mappings/revive/transfer.ts
  • src/mappings/uniques/create.ts
  • src/mappings/uniques/forceCreate.ts
  • src/mappings/uniques/getters/index.ts
  • src/mappings/utils/helper.ts
  • src/processable.ts
  • src/processor.ts
  • tests/index.test.ts
  • tests/revive.test.ts

Comment thread package.json
Comment thread src/mappings/index.ts
Comment thread src/mappings/revive/burn.ts
Comment thread src/mappings/revive/helpers.ts
Comment thread src/mappings/revive/helpers.ts
Comment thread src/mappings/revive/transfer.ts
Comment thread src/mappings/revive/transfer.ts
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (2)
tests/reviveHandlers.test.ts (2)

4-22: Unused mock functions defined.

pendingMock, successMock, calculateCollectionOwnerCountAndDistributionMock, and calculateCollectionFloorMock are hoisted but never asserted against in any test. Consider either:

  • Removing unused mocks to reduce noise, or
  • Adding tests for happy paths that would exercise these functions.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/reviveHandlers.test.ts` around lines 4 - 22, The hoisted mocks
pendingMock, successMock, calculateCollectionOwnerCountAndDistributionMock, and
calculateCollectionFloorMock are defined but never used; either remove these
unused mock variables from the vi.hoisted() block (leaving getOptionalMock,
getWithMock, warnMock, createEventMock as needed) or add unit tests that
exercise the paths invoking pendingMock, successMock,
calculateCollectionOwnerCountAndDistributionMock, and
calculateCollectionFloorMock (e.g., add a happy-path test that triggers the code
paths which call those mocks) so the mocks are asserted against; update
tests/reviveHandlers.test.ts by removing unused mock declarations or by adding
corresponding tests that call and assert on pendingMock, successMock,
calculateCollectionOwnerCountAndDistributionMock, and
calculateCollectionFloorMock.

108-125: Missing test: "skips burns for unknown collections"

The burn handler has the same unknown collection guard as the transfer handler (see burn.ts lines 17-22 in context snippet 2), but only the transfer handler has a test for this path. Consider adding symmetrical coverage:

➕ Proposed additional test
+  it('skips burns for unknown collections', async () => {
+    getOptionalMock.mockResolvedValue(undefined)
+
+    await handleTokenBurn(context, {
+      contractAddress: '0x329aaa5b6bea94e750b2dacba74bf41291e6c2bd',
+      from: '0x1111111111111111111111111111111111111111',
+      to: '0x0000000000000000000000000000000000000000',
+      tokenId: 1n,
+    })
+
+    expect(getWithMock).not.toHaveBeenCalled()
+    expect(warnMock).toHaveBeenCalledWith(
+      'BURN',
+      'Unknown collection 0x329aaa5b6bea94e750b2dacba74bf41291e6c2bd',
+    )
+  })
+
   it('skips burns when the nft was never indexed', async () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/reviveHandlers.test.ts` around lines 108 - 125, Add a parallel test for
the "unknown collection" path in the burn handler: mock getOptionalMock to
return undefined/null (simulating an unrecognized collection), call
handleTokenBurn with a burn event payload (use the same contractAddress/tokenId
pattern as other tests), and assert that warnMock is called for the burn
unknown-collection case and that createEventMock is not called; reference the
existing mocks getOptionalMock, getWithMock, warnMock, createEventMock and the
function handleTokenBurn to find where to add the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/reviveHandlers.test.ts`:
- Around line 4-22: The hoisted mocks pendingMock, successMock,
calculateCollectionOwnerCountAndDistributionMock, and
calculateCollectionFloorMock are defined but never used; either remove these
unused mock variables from the vi.hoisted() block (leaving getOptionalMock,
getWithMock, warnMock, createEventMock as needed) or add unit tests that
exercise the paths invoking pendingMock, successMock,
calculateCollectionOwnerCountAndDistributionMock, and
calculateCollectionFloorMock (e.g., add a happy-path test that triggers the code
paths which call those mocks) so the mocks are asserted against; update
tests/reviveHandlers.test.ts by removing unused mock declarations or by adding
corresponding tests that call and assert on pendingMock, successMock,
calculateCollectionOwnerCountAndDistributionMock, and
calculateCollectionFloorMock.
- Around line 108-125: Add a parallel test for the "unknown collection" path in
the burn handler: mock getOptionalMock to return undefined/null (simulating an
unrecognized collection), call handleTokenBurn with a burn event payload (use
the same contractAddress/tokenId pattern as other tests), and assert that
warnMock is called for the burn unknown-collection case and that createEventMock
is not called; reference the existing mocks getOptionalMock, getWithMock,
warnMock, createEventMock and the function handleTokenBurn to find where to add
the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0500119-7b22-4622-95dd-2cf0b508b9ea

📥 Commits

Reviewing files that changed from the base of the PR and between 8cc00ce and bd98c5e.

📒 Files selected for processing (5)
  • src/mappings/revive/burn.ts
  • src/mappings/revive/helpers.ts
  • src/mappings/revive/transfer.ts
  • tests/revive.test.ts
  • tests/reviveHandlers.test.ts
✅ Files skipped from review due to trivial changes (1)
  • tests/revive.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/mappings/revive/burn.ts
  • src/mappings/revive/transfer.ts
  • src/mappings/revive/helpers.ts

Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (1)
src/mappings/revive/updateCollectionMetadata.ts (1)

9-9: Consider avoiding as any for operation constant.

The as any cast suggests the logger's operation type doesn't include 'CONTRACT_METADATA'. Rather than bypassing type checking, consider extending the operation type union to include this new value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mappings/revive/updateCollectionMetadata.ts` at line 9, The OPERATION
constant uses a unsafe cast ("const OPERATION = 'CONTRACT_METADATA' as any");
instead, update the operation/type definition used by the logger to include
'CONTRACT_METADATA' (e.g., extend the Operation union/enum or add the new
literal to the existing type) and then declare OPERATION without "as any" (e.g.,
const OPERATION: Operation = 'CONTRACT_METADATA'), referencing OPERATION and the
logger's operation type/enum so the value is type-safe and no cast is needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/mappings/revive/updateCollectionMetadata.ts`:
- Line 34: The assignment to collection.updatedAt in updateCollectionMetadata
uses Date.now() as a fallback when context.block.timestamp is missing, causing
nondeterministic timestamps; change the behavior to match
toReviveBaseCall/helpers.ts by either throwing when context.block.timestamp is
undefined or substituting a deterministic value (e.g., a fixed epoch or derived
deterministic value), and update the collection.updatedAt assignment in
updateCollectionMetadata to rely on context.block.timestamp (or the chosen
deterministic alternative) instead of Date.now().

---

Nitpick comments:
In `@src/mappings/revive/updateCollectionMetadata.ts`:
- Line 9: The OPERATION constant uses a unsafe cast ("const OPERATION =
'CONTRACT_METADATA' as any"); instead, update the operation/type definition used
by the logger to include 'CONTRACT_METADATA' (e.g., extend the Operation
union/enum or add the new literal to the existing type) and then declare
OPERATION without "as any" (e.g., const OPERATION: Operation =
'CONTRACT_METADATA'), referencing OPERATION and the logger's operation type/enum
so the value is type-safe and no cast is needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3419ce69-2c97-432d-9f8d-04b95ca498e4

📥 Commits

Reviewing files that changed from the base of the PR and between bd98c5e and 6628bad.

📒 Files selected for processing (7)
  • src/mappings/revive/abi/erc721.ts
  • src/mappings/revive/helpers.ts
  • src/mappings/revive/index.ts
  • src/mappings/revive/updateCollectionMetadata.ts
  • src/processor.ts
  • tests/revive.test.ts
  • tests/reviveCollectionMetadata.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/mappings/revive/index.ts
  • src/processor.ts
  • src/mappings/revive/abi/erc721.ts
  • tests/revive.test.ts

Comment thread src/mappings/revive/updateCollectionMetadata.ts
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds initial Revive (EVM) indexing support (ERC-721) alongside existing Uniques/Nfts indexing, with Paseo-focused environment/config changes and schema updates to classify collection kinds.

Changes:

  • Introduce Revive event ingestion + handlers (collection register, mint, transfer, burn, contractURI updates) and an EVM RPC client via viem.
  • Add CollectionKind/collectionType to the data model + GraphQL schema and migrate existing rows.
  • Update environment/processor wiring (Paseo chain option, getter chain selection, Revive toggles) and add Vitest coverage for Revive flows.

Reviewed changes

Copilot reviewed 31 out of 35 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/reviveHandlers.test.ts Unit tests for Revive transfer/burn early-exit paths and warnings
tests/reviveCollectionMetadata.test.ts Unit tests for contractURI update handling & fallback behavior
tests/revive.test.ts Unit tests for Revive helper decoding/classification utilities
tests/index.test.ts Adds sanitizeText helper test and updates helper imports
src/processor.ts Wires Revive log ingestion into the processor behind REVIVE_ENABLED
src/processable.ts Adds Revive.ContractEmitted enum entry
src/model/generated/index.ts Exports generated CollectionKind enum
src/model/generated/collectionEntity.model.ts Adds collectionType column to CollectionEntity model
src/model/generated/_collectionKind.ts Defines CollectionKind enum (UNIQUES/NFTS/ERC721/ERC1155)
src/mappings/utils/helper.ts Switches SS58 encoding to use SS58_PREFIX from environment
src/mappings/uniques/getters/index.ts Uses GETTER_CHAIN for chain-specific getter resolution
src/mappings/uniques/forceCreate.ts Sets collectionType = UNIQUES on forced Unique collections
src/mappings/uniques/create.ts Sets collectionType = UNIQUES on created Unique collections
src/mappings/revive/updateCollectionMetadata.ts Implements handler for ContractURIUpdated updates and metadata ingestion
src/mappings/revive/transfer.ts Implements Revive ERC-721 transfer handler
src/mappings/revive/mint.ts Implements Revive ERC-721 mint handler
src/mappings/revive/index.ts Dispatches Revive contract-emitted logs by topic0
src/mappings/revive/helpers.ts Adds log payload parsing, event decoding, and ID/address helpers
src/mappings/revive/createCollection.ts Creates/updates collections on registry CollectionRegistered
src/mappings/revive/client.ts Adds viem public client + contract read helpers
src/mappings/revive/burn.ts Implements Revive ERC-721 burn handler
src/mappings/revive/abi/registry.ts Registry ABI + event topic selector
src/mappings/revive/abi/erc721.ts ERC-721 ABI + event topic selectors
src/mappings/nfts/getters/index.ts Uses GETTER_CHAIN for chain-specific getter resolution
src/mappings/nfts/forceCreate.ts Sets collectionType = NFTS on forced Nfts collections
src/mappings/nfts/create.ts Sets collectionType = NFTS on created Nfts collections
src/mappings/index.ts Adds Revive handler routing + changes one-time asset forcing logic
src/mappings/assets/getters/index.ts Uses GETTER_CHAIN for chain-specific getter resolution
src/mappings/assets/forceToken.ts Updates system asset defaults to Paseo (PAS)
src/environment.ts Adds Paseo chain option, Revive toggles, start block logic, getter chain + SS58 prefix
snack.yaml Adds a subsquid manifest configured for AssetHub Paseo + Revive
schema.graphql Adds collectionType: CollectionKind! and defines CollectionKind enum
package.json Adds viem dependency
package-lock.json Locks viem and transitive deps
db/migrations/1774691683926-Data.js Migrates collection_entity.collection_type + backfills/indexes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/index.test.ts
Comment thread src/mappings/index.ts
Copy link
Copy Markdown

@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

🧹 Nitpick comments (4)
src/mappings/revive/helpers.ts (1)

69-120: Consider strict: true for decodeEventLog (optional).

With strict: false, if a payload's data is empty/mismatched for a non-indexed field, the corresponding entry in decoded.args becomes undefined, and String(undefined) silently yields the literal "undefined" string (e.g., normalizeEvmAddress('undefined') would return "undefined" rather than throwing). For well-formed on-chain events this is a non-issue, but strict: true would surface malformed payloads early with a clearer error instead of persisting bogus values.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mappings/revive/helpers.ts` around lines 69 - 120, The decodeEventLog
calls in decodeCollectionRegisteredEvent, decodeTransferEvent, and
decodeContractUriUpdatedEvent use strict: false which can let malformed logs
produce undefined values (e.g., String(undefined)); change strict to true in
each decodeEventLog invocation to force validation and surface malformed
payloads early, and if needed wrap the calls in try/catch at the call site or
let the thrown error propagate so malformed events are rejected instead of
producing bogus values.
tests/revive.test.ts (1)

16-120: LGTM — good coverage of helpers.

Encoding via viem's encodeEventTopics/encodeAbiParameters keeps the tests realistic, and covering the negative path (empty topics) plus address normalization is sufficient for these helpers.

Optional: add a case for isMintTransfer/isBurnTransfer with a mixed-case zero address (e.g., 0x0000...0000 with uppercase prefix or padded variants from topics) to exercise normalizeEvmAddress behavior inside those predicates.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/revive.test.ts` around lines 16 - 120, Add a test that verifies
isMintTransfer and isBurnTransfer handle mixed-case/padded zero addresses by
normalizing them; update the 'classifies mint and burn transfers' spec to
include at least one assertion calling
isMintTransfer('0x0000000000000000000000000000000000000000' in mixed-case like
"0x0000000000000000000000000000000000000000".toUpperCase() or a
padded/mixed-case variant) and the same for isBurnTransfer, ensuring the
predicates internally normalize via normalizeEvmAddress and still return true
for the zero address and false for non-zero variants (use the existing
expect(...) .toBe(...) pattern with isMintTransfer and isBurnTransfer).
src/mappings/nfts/setAttribute.ts (1)

38-48: Nit: human alias is redundant.

const human = value in the catch just re-aliases the already-sanitized value from line 20 — you can drop it and reference value directly. Non-blocking.

Proposed tidy-up
     } catch (error) {
-      const human = value
-      final.recipient = isAddress(human) ? human : ''
+      final.recipient = isAddress(value) ? value : ''
       if (final.recipient === '') {
         console.log(error)
       }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mappings/nfts/setAttribute.ts` around lines 38 - 48, The catch block in
the conditional that checks 'recipient' currently creates a redundant alias
const human = value; remove that alias and use the already-sanitized value
variable directly when calling isAddress and assigning final.recipient (keep the
surrounding try/catch and the addressOf(event.value as string) logic intact, and
still console.log(error) when final.recipient becomes an empty string).
tests/nftSetAttribute.test.ts (1)

53-86: Consider adding a couple more cases to lock down behavior you’re now relying on.

The single test covers the happy NFT path with a non-null, sanitizable value. Worthwhile additions while you’re here:

  1. Null-value path — assert that when event.value === null, the existing matching attribute is removed (the filter branch at line 51). This is the code path that actually exercises sanitizeText(trait) independently from value.
  2. Collection path — when isNFT(event) is false, markCollectionRarityDirty/markNftAttributesDirty must not be called. Adding expect(...).not.toHaveBeenCalled() guards against regressing this invariant.
  3. Special-trait exclusion — feeding trait: 'royalty' or trait: 'recipient' should not push a new entry into attributes (line 56 guard).

These are cheap to add and codify the most subtle branches of the handler.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/nftSetAttribute.test.ts` around lines 53 - 86, Add three new test cases
around handleAttributeSet: (1) a "null-value path" test where currentEvent.value
=== null and an existing attribute is removed—assert nft.attributes no longer
contains the trait and sanitizeText(trait) is still applied; (2) a "collection
path" test where isNFT(event) returns false and assert markCollectionRarityDirty
and markNftAttributesDirty were not called; and (3) a "special-trait exclusion"
test that calls handleAttributeSet with trait 'royalty' and/or 'recipient' and
assert that no new attribute is pushed into nft.attributes. Ensure each test
mocks getMock, store.save, and relevant dirtiness mocks and asserts store.save
behavior and attribute array contents accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/mappings/nfts/setAttribute.ts`:
- Around line 19-20: The current assignment for value only checks event.value
=== null and lets undefined become the string "undefined"; change the null check
to a nullish check (e.g., event.value == null or use the nullish coalescing
pattern) so both null and undefined yield null, and simplify the branch by
calling sanitizeText(unHex(event.value)) only when event.value is not nullish;
update the value assignment in setAttribute.ts (referencing value, sanitizeText,
and unHex) accordingly.

In `@src/mappings/utils/helper.ts`:
- Around line 5-9: The codec is currently set from SS58_PREFIX which maps
non-kusama chains (including Paseo) to 0, causing addressOf() to encode Paseo
addresses with Polkadot's prefix; update the logic so Paseo uses its real
prefix: either restore codec derivation from the CHAIN string (use CHAIN to
select the SS58 codec) or change SS58_PREFIX computation to explicitly map CHAIN
=== 'paseo' to 42 (e.g. CHAIN === 'kusama' ? 2 : CHAIN === 'paseo' ? 42 : 0),
and ensure codec in helper.ts is assigned from that corrected SS58_PREFIX so
addressOf() produces correct Paseo addresses.

---

Nitpick comments:
In `@src/mappings/nfts/setAttribute.ts`:
- Around line 38-48: The catch block in the conditional that checks 'recipient'
currently creates a redundant alias const human = value; remove that alias and
use the already-sanitized value variable directly when calling isAddress and
assigning final.recipient (keep the surrounding try/catch and the
addressOf(event.value as string) logic intact, and still console.log(error) when
final.recipient becomes an empty string).

In `@src/mappings/revive/helpers.ts`:
- Around line 69-120: The decodeEventLog calls in
decodeCollectionRegisteredEvent, decodeTransferEvent, and
decodeContractUriUpdatedEvent use strict: false which can let malformed logs
produce undefined values (e.g., String(undefined)); change strict to true in
each decodeEventLog invocation to force validation and surface malformed
payloads early, and if needed wrap the calls in try/catch at the call site or
let the thrown error propagate so malformed events are rejected instead of
producing bogus values.

In `@tests/nftSetAttribute.test.ts`:
- Around line 53-86: Add three new test cases around handleAttributeSet: (1) a
"null-value path" test where currentEvent.value === null and an existing
attribute is removed—assert nft.attributes no longer contains the trait and
sanitizeText(trait) is still applied; (2) a "collection path" test where
isNFT(event) returns false and assert markCollectionRarityDirty and
markNftAttributesDirty were not called; and (3) a "special-trait exclusion" test
that calls handleAttributeSet with trait 'royalty' and/or 'recipient' and assert
that no new attribute is pushed into nft.attributes. Ensure each test mocks
getMock, store.save, and relevant dirtiness mocks and asserts store.save
behavior and attribute array contents accordingly.

In `@tests/revive.test.ts`:
- Around line 16-120: Add a test that verifies isMintTransfer and isBurnTransfer
handle mixed-case/padded zero addresses by normalizing them; update the
'classifies mint and burn transfers' spec to include at least one assertion
calling isMintTransfer('0x0000000000000000000000000000000000000000' in
mixed-case like "0x0000000000000000000000000000000000000000".toUpperCase() or a
padded/mixed-case variant) and the same for isBurnTransfer, ensuring the
predicates internally normalize via normalizeEvmAddress and still return true
for the zero address and false for non-zero variants (use the existing
expect(...) .toBe(...) pattern with isMintTransfer and isBurnTransfer).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 29116d15-80b7-4a95-afa9-7edd390321c0

📥 Commits

Reviewing files that changed from the base of the PR and between 5a125ce and 488c2ba.

📒 Files selected for processing (6)
  • src/mappings/index.ts
  • src/mappings/nfts/setAttribute.ts
  • src/mappings/revive/helpers.ts
  • src/mappings/utils/helper.ts
  • tests/nftSetAttribute.test.ts
  • tests/revive.test.ts

Comment thread src/mappings/nfts/setAttribute.ts Outdated
Comment thread src/mappings/utils/helper.ts
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 35 out of 39 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 18 to 38
@@ -26,14 +27,14 @@ export async function handleAttributeSet(context: Context): Promise<void> {
final.attributes = []
}

if (event.value === null) {
if (value === null) {
final.attributes = final.attributes?.filter((attr) => attr.trait !== event.trait)
} else {
const attribute = final.attributes?.find((attr) => attr.trait === event.trait)
if (attribute) {
attribute.value = unHex(event.value) ?? String(event.value)
attribute.value = value
} else {
const newAttribute = attributeFrom({ trait_type: event.trait, value: unHex(event.value) ?? String(event.value) })
const newAttribute = attributeFrom({ trait_type: event.trait, value })
final.attributes?.push(newAttribute)
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

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

sanitizeText was introduced to strip NUL characters that Postgres cannot store, and nfts/setAttribute now uses it. uniques/setAttribute still persists event.trait and event.value (via attribute.value = value / attributeFrom) without sanitization, so any Uniques attribute containing \u0000 will still cause a DB write failure. Consider applying sanitizeText to both the trait and decoded value here (similar to the Nfts handler) before comparing/storing.

Copilot uses AI. Check for mistakes.
Comment thread src/processor.ts
Comment on lines +132 to +140
processor.addReviveContractEmitted({
topic0: [ERC721_TRANSFER_TOPIC],
range: { from: REVIVE_START_BLOCK },
})

processor.addReviveContractEmitted({
topic0: [CONTRACT_URI_UPDATED_TOPIC],
range: { from: REVIVE_START_BLOCK },
})
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

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

The addReviveContractEmitted registrations for ERC721_TRANSFER_TOPIC and CONTRACT_URI_UPDATED_TOPIC do not specify a contract filter, so the processor will ingest all Revive ContractEmitted logs matching those topics, regardless of whether the contract is one you index. Given the handlers currently warn on unknown collections/NFTs, this can create significant ingestion/log volume on chains with unrelated Revive ERC-721 activity. If possible, consider adding a contract allowlist (e.g., env-provided addresses) or reducing log severity for expected “unknown” skips to avoid operational noise.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants