Skip to content

Shape registry implementation#2171

Draft
chlwys wants to merge 1 commit intomainfrom
ap.shape_registry_system
Draft

Shape registry implementation#2171
chlwys wants to merge 1 commit intomainfrom
ap.shape_registry_system

Conversation

@chlwys
Copy link
Contributor

@chlwys chlwys commented Nov 4, 2025

Shape registry: To store top level data for specific shapes. For example, if KAMI can be transferred internally, or if INVENTORY can be traded.

  • Useful for multi-shape systems, like the to-be-implemented marketplace. A shape registry contains flags for all entities of that shape - sort of a workaround to use purist ECS patterns while minimizing writes on chain
  • Cleaner data storage, storing shape data via string (EntityType) instead of raw componentIDs

Summary by CodeRabbit

  • New Features
    • Introduced Shape Registry System to register, flag, associate owners, remove, and deprecate shapes.
    • Added create/typed-create interfaces plus execute APIs.
    • Added ownership transfer/handover flows and admin-restricted operations.
  • Client SDK
    • Added ABI, strongly-typed contract bindings, and public exports for Shape Registry to enable safe, compile-time interactions.

@vercel
Copy link

vercel bot commented Nov 4, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
kamigotchi-client Ready Ready Preview Comment Nov 13, 2025 9:43am

Copy link
Contributor Author

chlwys commented Nov 4, 2025

This stack of pull requests is managed by Graphite. Learn more about stacking.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 4, 2025

Walkthrough

This PR adds a new Shape Registry System across the codebase: ABI, TypeScript/ethers contract bindings, client mappings, deployment bytecodes/mappings, a Solidity library (LibShapeRegistry) implementing registry logic, and an admin-only system contract (_ShapeRegistrySystem) exposing create/remove/flag/owner-comp operations.

Changes

Cohort / File(s) Summary
ABI Definition
packages/client/abi/_ShapeRegistrySystem.json
New ABI JSON for Shape Registry System (constructor, execute/executeTyped, addFlag, addOwnerComp, remove, deprecate, ownership management functions/events/errors, and methodIdentifier map).
Client ABI & Exports
packages/client/types/SystemAbis.mjs, packages/client/types/ethers-contracts/index.ts
Imported and exported the Shape Registry ABI and ethers types; added _ShapeRegistrySystem type and _ShapeRegistrySystem__factory to public exports.
Client System Mappings & Types
packages/client/types/SystemMappings.ts, packages/client/types/SystemTypes.ts
Added bidirectional mapping between _ShapeRegistrySystem and id "system.shape.registry" and registered the typed contract in SystemTypes.
Client Ethers Contract Typings
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts
Added autogenerated TypeScript typings for _ShapeRegistrySystem: strongly-typed functions, TypedContractMethod signatures, events/namespaces, filters, and encode/decode helpers.
Deployment Configuration
packages/contracts/deploy.json
Added _ShapeRegistrySystem entry to systems array with specified writeAccess fields.
Deployment Bytecodes & Mappings
packages/contracts/deployment/contracts/mappings/SystemBytecodes.ts, packages/contracts/deployment/contracts/mappings/SystemMappings.ts
Imported compiled _ShapeRegistrySystem bytecode and added mapping "system.shape.registry": _ShapeRegistrySystem in bytecodes and system/id mappings.
Solidity Library
packages/contracts/src/libraries/LibShapeRegistry.sol
New library implementing registry domain logic: create, addOwnerComp, addFlag, remove, isInstance, get, and deterministic genID; interacts with Type/Name/Description/IdHolder/IsRegistry components and LibFlag/LibEntityType.
Solidity System Contract
packages/contracts/src/systems/_ShapeRegistrySystem.sol
New admin-restricted system contract delegating to LibShapeRegistry: execute/executeTyped, addOwnerComp, addFlag, remove, and deprecate; constructor wires world and components and enforces onlyAdmin(components).

Sequence Diagram(s)

sequenceDiagram
    participant Admin
    participant ShapeSystem as _ShapeRegistrySystem
    participant Lib as LibShapeRegistry
    participant Comps as Components

    Admin->>ShapeSystem: execute(bytes arguments)
    activate ShapeSystem
    ShapeSystem->>ShapeSystem: decode(type_, description)
    ShapeSystem->>Lib: create(comps, type_, description)
    activate Lib
    Lib->>Lib: genID(type_)
    Lib->>Comps: write Type, Name, Description, IsRegistry, IdHolder
    Lib-->>ShapeSystem: id
    deactivate Lib
    ShapeSystem-->>Admin: return encoded bytes
    deactivate ShapeSystem

    Admin->>ShapeSystem: addOwnerComp(type_, ownerCompID)
    activate ShapeSystem
    ShapeSystem->>Lib: get(comps, type_)
    Lib-->>ShapeSystem: id
    ShapeSystem->>Lib: addOwnerComp(comps, id, ownerCompID)
    Lib->>Comps: set IdHolder
    deactivate ShapeSystem

    Admin->>ShapeSystem: remove(type_)
    activate ShapeSystem
    ShapeSystem->>Lib: get(comps, type_)
    Lib-->>ShapeSystem: id
    ShapeSystem->>Lib: remove(comps, id)
    Lib->>Comps: clear Type, Name, Description, IsRegistry, IdHolder, flags
    deactivate ShapeSystem
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Areas to focus on:
    • LibShapeRegistry: deterministic ID generation (genID), component writes and removals, and flag cleanup.
    • _ShapeRegistrySystem: access control (onlyAdmin), argument encoding/decoding, and error handling when shapes don't exist.
    • Consistency between ABI, TypeScript typings, and deployment mappings/bytecodes.

Possibly related PRs

  • update pnpm lock #2168 — modifies the ethers-contracts index export surface and overlaps with factory export changes (including _ShapeRegistrySystem).

Suggested reviewers

  • JirAcheron

Poem

🐰
I hop and stamp a tiny trace,
A shape appears, a named new place,
Flags and owners snugly fit,
Admins call bytes and codes commit,
Rabbit nods — the registry is in grace.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Shape registry implementation' clearly and concisely describes the main change: adding a new shape registry system to store metadata for shapes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ap.shape_registry_system

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58c3346 and 3bd12d0.

📒 Files selected for processing (11)
  • packages/client/abi/_ShapeRegistrySystem.json (1 hunks)
  • packages/client/types/SystemAbis.mjs (2 hunks)
  • packages/client/types/SystemMappings.ts (2 hunks)
  • packages/client/types/SystemTypes.ts (2 hunks)
  • packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1 hunks)
  • packages/client/types/ethers-contracts/index.ts (4 hunks)
  • packages/contracts/deploy.json (1 hunks)
  • packages/contracts/deployment/contracts/mappings/SystemBytecodes.ts (2 hunks)
  • packages/contracts/deployment/contracts/mappings/SystemMappings.ts (2 hunks)
  • packages/contracts/src/libraries/LibShapeRegistry.sol (1 hunks)
  • packages/contracts/src/systems/_ShapeRegistrySystem.sol (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/client/types/SystemTypes.ts
  • packages/client/abi/_ShapeRegistrySystem.json
  • packages/contracts/deploy.json
  • packages/contracts/deployment/contracts/mappings/SystemMappings.ts
  • packages/client/types/SystemAbis.mjs
  • packages/client/types/SystemMappings.ts
  • packages/contracts/deployment/contracts/mappings/SystemBytecodes.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1)
packages/client/types/ethers-contracts/index.ts (1)
  • _ShapeRegistrySystem (201-201)
🪛 Biome (2.1.2)
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts

[error] 172-172: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (12)
packages/client/types/ethers-contracts/index.ts (1)

201-201: LGTM! Proper integration of ShapeRegistrySystem exports.

The autogenerated index correctly exports the new _ShapeRegistrySystem type and associated factories, along with the related component factories. The integration is consistent with the existing pattern.

Also applies to: 209-230, 285-285, 297-298, 311-311

packages/contracts/src/systems/_ShapeRegistrySystem.sol (4)

22-27: LGTM! Clean typed wrapper.

The executeTyped function provides a convenient typed interface for shape registry creation.


29-33: LGTM! Proper validation before mutation.

The function correctly validates shape existence before adding the owner component.


35-39: LGTM! Consistent validation pattern.

The function follows the same validation pattern as addOwnerComp.


41-45: LGTM! Safe deletion with validation.

The function ensures the shape exists before attempting removal.

packages/contracts/src/libraries/LibShapeRegistry.sol (5)

41-43: LGTM! Simple and correct.

The function cleanly stores the owner component ID.


45-47: LGTM! Proper delegation to LibFlag.

The function correctly delegates flag management to the flag library.


49-59: LGTM! Complete cleanup including registry flag.

The function now properly removes all components including the IsRegistryComponent flag (line 55), which addresses the previous review concern.


64-66: LGTM! Clean instance check.

The function delegates to LibEntityType for type validation.


71-81: LGTM! Clean getter and ID generation.

Both get and genID follow standard patterns with deterministic ID generation and safe querying.

packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (2)

26-130: LGTM! Comprehensive interface definition.

The autogenerated interface provides complete type coverage for all contract functions and events.


179-402: LGTM! Complete contract interface with strong typing.

The autogenerated contract interface provides comprehensive typed access to all ShapeRegistrySystem functions, events, and filters.


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.

@chlwys chlwys changed the title Shape registry system Shape registry implementation Nov 4, 2025
@chlwys chlwys marked this pull request as ready for review November 4, 2025 13:14
@chlwys chlwys requested a review from JirAcheron as a code owner November 4, 2025 13:14
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 780f861 and 6ddc623.

📒 Files selected for processing (11)
  • packages/client/abi/_ShapeRegistrySystem.json (1 hunks)
  • packages/client/types/SystemAbis.mjs (2 hunks)
  • packages/client/types/SystemMappings.ts (2 hunks)
  • packages/client/types/SystemTypes.ts (2 hunks)
  • packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1 hunks)
  • packages/client/types/ethers-contracts/index.ts (2 hunks)
  • packages/contracts/deploy.json (1 hunks)
  • packages/contracts/deployment/contracts/mappings/SystemBytecodes.ts (2 hunks)
  • packages/contracts/deployment/contracts/mappings/SystemMappings.ts (2 hunks)
  • packages/contracts/src/libraries/LibShapeRegistry.sol (1 hunks)
  • packages/contracts/src/systems/_ShapeRegistrySystem.sol (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
packages/contracts/deployment/contracts/mappings/SystemBytecodes.ts (2)
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1)
  • _ShapeRegistrySystem (173-383)
packages/client/types/ethers-contracts/index.ts (1)
  • _ShapeRegistrySystem (201-201)
packages/client/types/SystemAbis.mjs (2)
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1)
  • _ShapeRegistrySystem (173-383)
packages/client/types/ethers-contracts/index.ts (1)
  • _ShapeRegistrySystem (201-201)
packages/client/types/SystemTypes.ts (2)
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1)
  • _ShapeRegistrySystem (173-383)
packages/client/types/ethers-contracts/index.ts (1)
  • _ShapeRegistrySystem (201-201)
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1)
packages/client/types/ethers-contracts/index.ts (1)
  • _ShapeRegistrySystem (201-201)
🪛 Biome (2.1.2)
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts

[error] 166-166: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

Comment on lines +163 to +171
export namespace SystemDeprecatedEvent {
export type InputTuple = [];
export type OutputTuple = [];
export interface OutputObject {}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Replace empty interface with type alias

Biome flags empty interfaces as errors (lint/suspicious/noEmptyInterface). Switch this to a type alias so the generated typings pass lint.

-export namespace SystemDeprecatedEvent {
-  export type InputTuple = [];
-  export type OutputTuple = [];
-  export interface OutputObject {}
+export namespace SystemDeprecatedEvent {
+  export type InputTuple = [];
+  export type OutputTuple = [];
+  export type OutputObject = Record<string, never>;
📝 Committable suggestion

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

Suggested change
export namespace SystemDeprecatedEvent {
export type InputTuple = [];
export type OutputTuple = [];
export interface OutputObject {}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace SystemDeprecatedEvent {
export type InputTuple = [];
export type OutputTuple = [];
export type OutputObject = Record<string, never>;
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 166-166: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🤖 Prompt for AI Agents
In packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts around lines
163 to 171, the generated empty interface "OutputObject {}" triggers the lint
rule lint/suspicious/noEmptyInterface; replace that empty interface with a type
alias (e.g., "export type OutputObject = {}") so the typings satisfy the linter
while keeping the same shape and exported name.

Comment on lines +16 to +20
function execute(bytes memory arguments) public onlyAdmin(components) returns (bytes memory) {
(string memory type_, string memory description) = abi.decode(arguments, (string, string));
LibShapeRegistry.create(components, type_, description);
return "";
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add the missing override specifier.

System.execute(bytes) is declared virtual; without override this implementation fails to compile because the abstract base method remains unresolved. Please tag the function as an override so the contract builds.

-  function execute(bytes memory arguments) public onlyAdmin(components) returns (bytes memory) {
+  function execute(bytes memory arguments) public override onlyAdmin(components) returns (bytes memory) {
🤖 Prompt for AI Agents
In packages/contracts/src/systems/_ShapeRegistrySystem.sol around lines 16 to
20, the execute(bytes) implementation is missing the required override
specifier; update the function signature to include the override keyword (e.g.,
function execute(bytes memory arguments) public override onlyAdmin(components)
returns (bytes memory)) so it correctly overrides the virtual
System.execute(bytes) declaration and the contract will compile.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/contracts/src/systems/_ShapeRegistrySystem.sol (1)

16-20: Add the missing override specifier.

The execute function must include the override keyword to properly override the virtual System.execute(bytes) method. Without it, the contract will fail to compile.

Apply this diff:

-  function execute(bytes memory arguments) public onlyAdmin(components) returns (bytes memory) {
+  function execute(bytes memory arguments) public override onlyAdmin(components) returns (bytes memory) {
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1)

169-177: Replace empty interface with type alias to satisfy linter.

The empty OutputObject interface triggers Biome's lint/suspicious/noEmptyInterface error. Replace it with a type alias.

Apply this diff:

 export namespace SystemDeprecatedEvent {
   export type InputTuple = [];
   export type OutputTuple = [];
-  export interface OutputObject {}
+  export type OutputObject = Record<string, never>;
   export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
   export type Filter = TypedDeferredTopicFilter<Event>;
   export type Log = TypedEventLog<Event>;
   export type LogDescription = TypedLogDescription<Event>;
 }
🧹 Nitpick comments (1)
packages/contracts/src/libraries/LibShapeRegistry.sol (1)

41-43: Consider guarding against overwriting existing owner component.

The function allows silently overwriting an existing owner component ID. While the system layer validates existence, consider whether replacing an owner component should emit a warning or require explicit confirmation.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ddc623 and 58c3346.

📒 Files selected for processing (11)
  • packages/client/abi/_ShapeRegistrySystem.json (1 hunks)
  • packages/client/types/SystemAbis.mjs (2 hunks)
  • packages/client/types/SystemMappings.ts (2 hunks)
  • packages/client/types/SystemTypes.ts (2 hunks)
  • packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1 hunks)
  • packages/client/types/ethers-contracts/index.ts (2 hunks)
  • packages/contracts/deploy.json (1 hunks)
  • packages/contracts/deployment/contracts/mappings/SystemBytecodes.ts (2 hunks)
  • packages/contracts/deployment/contracts/mappings/SystemMappings.ts (2 hunks)
  • packages/contracts/src/libraries/LibShapeRegistry.sol (1 hunks)
  • packages/contracts/src/systems/_ShapeRegistrySystem.sol (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/contracts/deployment/contracts/mappings/SystemMappings.ts
  • packages/contracts/deployment/contracts/mappings/SystemBytecodes.ts
  • packages/client/types/SystemAbis.mjs
  • packages/contracts/deploy.json
  • packages/client/types/SystemMappings.ts
  • packages/client/types/SystemTypes.ts
  • packages/client/types/ethers-contracts/index.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts (1)
packages/client/types/ethers-contracts/index.ts (1)
  • _ShapeRegistrySystem (201-201)
🪛 Biome (2.1.2)
packages/client/types/ethers-contracts/_ShapeRegistrySystem.ts

[error] 172-172: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (5)
packages/contracts/src/libraries/LibShapeRegistry.sol (5)

45-47: LGTM!

The flag addition logic correctly delegates to LibFlag.setFull with the registry type and flag type.


49-59: LGTM! Past issue has been addressed.

The remove function now properly clears the IsRegistryComponent flag (line 55), addressing the previous review concern. All associated components and flags are cleaned up correctly.


64-66: LGTM!

The instance checker correctly delegates to LibEntityType.isShape.


71-74: LGTM!

The getter correctly uses the sentinel value 0 to indicate non-existent registries, which aligns with the system's existence checks.


79-81: LGTM!

The deterministic ID generation using a namespaced prefix is appropriate for collision avoidance.

Comment on lines +28 to +39
function create(
IUintComp comps,
string memory type_,
string memory description
) internal returns (uint256 id) {
id = genID(type_);
LibEntityType.set(comps, id, "SHAPE_REGISTRY");
IsRegistryComponent(getAddrByID(comps, IsRegistryCompID)).set(id);
TypeComponent(getAddrByID(comps, TypeCompID)).set(id, type_);
NameComponent(getAddrByID(comps, NameCompID)).set(id, type_);
DescriptionComponent(getAddrByID(comps, DescriptionCompID)).set(id, description);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add existence check to prevent overwriting existing registries.

The create function does not verify whether a registry with the given type_ already exists. Calling it twice with the same type will silently overwrite the existing entry, potentially losing associated flags or owner component mappings.

Consider adding a check:

  function create(
    IUintComp comps,
    string memory type_,
    string memory description
  ) internal returns (uint256 id) {
    id = genID(type_);
+   require(!isInstance(comps, id), "ShapeRegistry: already exists");
    LibEntityType.set(comps, id, "SHAPE_REGISTRY");
    IsRegistryComponent(getAddrByID(comps, IsRegistryCompID)).set(id);
    TypeComponent(getAddrByID(comps, TypeCompID)).set(id, type_);
    NameComponent(getAddrByID(comps, NameCompID)).set(id, type_);
    DescriptionComponent(getAddrByID(comps, DescriptionCompID)).set(id, description);
  }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/contracts/src/libraries/LibShapeRegistry.sol around lines 28 to 39,
the create(...) function currently generates an id and proceeds to set registry
components without checking for an existing registry, which allows silent
overwrites; modify the function to compute id = genID(type_) then check whether
the registry already exists (e.g.,
require(!IsRegistryComponent(getAddrByID(comps, IsRegistryCompID)).get(id),
"Shape registry exists") or equivalent using the TypeComponent) and revert if it
does, before performing any set(...) calls so existing registries and their
associated flags/owners are preserved.

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.

1 participant