Skip to content

Conversation

@MartianGreed
Copy link
Collaborator

@MartianGreed MartianGreed commented Dec 20, 2025

Closes #

Introduced changes

Checklist

  • Linked relevant issue
  • Updated relevant documentation
  • Added relevant tests
  • Add a dedicated CI job for new examples
  • Performed self-review of the code

Summary by CodeRabbit

  • Tests

    • Added end-to-end tests covering ABI function resolution and validation.
  • New Features

    • Runtime calldata compilation for contract calls.
    • Automatic method-name disambiguation when duplicate system names exist.
    • Improved validation and clearer error messages for contract invocations.
  • Chores

    • Added release changeset documenting the core patch.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 20, 2025

Walkthrough

Adds ABI-based calldata compilation and function-ABI resolution; the Dojo provider now disambiguates duplicate system names at runtime and compiles calldata before invoking contracts; tests for ABI resolution were added.

Changes

Cohort / File(s) Summary
Compilation utilities
packages/core/src/utils/compile.ts
Adds findFunctionAbiByNamespace (ABI function resolution by namespace/contract/function with multiple strategies) and compileDojoCalldata (normalizes args, validates required inputs, parses fields, returns calldata with a non-enumerable __compiled__ marker).
Provider integration
packages/core/src/provider/DojoProvider.ts
Integrates compileDojoCalldata into call flow, computes functionCounts to disambiguate duplicate system names, generates dynamic host methods using the derived methodName, and updates error messages to reference methodName.
Utils index update
packages/core/src/utils/index.ts
Replaces CallData.compile usage with compileDojoCalldata in parseDojoCall and updates imports accordingly.
Tests
packages/core/src/_test_/compile.test.ts
Adds Vitest suite exercising findFunctionAbiByNamespace against manifest data, validating multiple interfaces, input names/lengths, fallback resolution, and nonexistent-function behavior.
Changeset
.changeset/yellow-garlics-judge.md
Adds a patch changeset noting the provider improvements and internalized calldata compiler.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Provider as DojoProvider
    participant Compiler as compileDojoCalldata
    participant Resolver as findFunctionAbiByNamespace
    participant Contract as StarkNet Contract

    Client->>Provider: invoke action (namespace, contractName, systemName, args)
    Provider->>Provider: compute functionCounts → derive methodName (disambiguate)
    Provider->>Compiler: compileDojoCalldata(abi, namespace, contractName, systemName, args)
    Compiler->>Resolver: findFunctionAbiByNamespace(abi, namespace, contractName, systemName)
    Resolver-->>Compiler: return FunctionAbi (or undefined)
    alt FunctionAbi found
        Compiler->>Compiler: normalize args, validate required inputs, parse fields
        Compiler-->>Provider: return compiled calldata (with __compiled__ flag)
        Provider->>Contract: call(methodName, compiled calldata)
        Contract-->>Provider: result
        Provider-->>Client: return result
    else Not found / validation error
        Compiler-->>Provider: throw error
        Provider-->>Client: propagate error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Attention points:
    • packages/core/src/utils/compile.ts: matching strategies and argument validation paths.
    • packages/core/src/provider/DojoProvider.ts: dynamic method naming, functionCounts computation, and updated call path.
    • Tests: ensure test fixtures align with ABI matching expectations.

Poem

🐰 I hopped through ABIs, nose in the code,
Compiled the calldata down every road.
When names collided, I stitched them true,
Now Dojo knows just what to do.
Happy hops — a small build bestowed.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete with only template placeholders, no actual issue linked, and no description of the changes made despite substantial code modifications. Fill in the 'Introduced changes' section with a summary of the new calldata compilation utilities and provider improvements. Link the relevant issue, confirm tests were added, and mark completed checklist items.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: improving the provider and internalizing the calldata compiler functionality, which aligns with the core changes in DojoProvider.ts and compile.ts.
✨ 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 feat/compile-calldata

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

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

Caution

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

⚠️ Outside diff range comments (1)
packages/core/src/utils/index.ts (1)

37-54: Potential null pointer dereference if contract not found.

getContractByName uses Array.find() which returns undefined if no match is found. If the contract doesn't exist in the manifest, accessing contract.address and contract.abi on lines 45-47 will throw a runtime error.

🔎 Proposed fix with guard clause
 export const parseDojoCall = (
     manifest: any,
     nameSpace: string,
     call: DojoCall | Call
 ): Call => {
     if ("contractName" in call) {
         const contract = getContractByName(
             manifest,
             nameSpace,
             call.contractName
         );
+
+        if (!contract) {
+            throw new Error(
+                `Contract "${call.contractName}" not found in namespace "${nameSpace}"`
+            );
+        }

         return {
             contractAddress: contract.address,
             calldata: compileDojoCalldata(
🧹 Nitpick comments (4)
packages/core/src/_test_/compile.test.ts (1)

7-76: Consider adding tests for compileDojoCalldata.

The test suite covers findFunctionAbiByNamespace well with multiple scenarios including the fallback behavior and nonexistent function case. However, compileDojoCalldata is also a new exported function with error-throwing paths (missing method, missing arguments) that would benefit from test coverage.

🔎 Example test cases for compileDojoCalldata
describe("compileDojoCalldata", () => {
    it("should throw when method not found", () => {
        expect(() =>
            compileDojoCalldata(
                abi,
                "pistols",
                "admin",
                "nonexistent_method",
                {}
            )
        ).toThrow(/Method "nonexistent_method" not found/);
    });

    it("should throw when required argument is missing", () => {
        expect(() =>
            compileDojoCalldata(
                abi,
                "pistols",
                "admin",
                "set_paused",
                {} // missing "paused" argument
            )
        ).toThrow(/Missing argument "paused"/);
    });

    it("should compile calldata with valid arguments", () => {
        const result = compileDojoCalldata(
            abi,
            "pistols",
            "admin",
            "set_paused",
            { paused: true }
        );
        expect(result).toBeDefined();
        expect(Array.isArray(result)).toBe(true);
    });
});
packages/core/src/provider/DojoProvider.ts (1)

358-400: Well-designed duplicate detection with a minor naming edge case.

The logic for detecting duplicate system names and generating disambiguated method names is sound. However, using underscores as separators in ${names.namespace}_${names.contractName}_${systemName} could cause collisions if namespace or contractName contain underscores themselves.

For example:

  • namespace="foo_bar", contractName="baz"foo_bar_baz_systemName
  • namespace="foo", contractName="bar_baz"foo_bar_baz_systemName

This is likely rare in practice, but consider using a more distinct separator (e.g., :: or __) if Dojo naming conventions allow underscores.

packages/core/src/utils/compile.ts (2)

22-82: Consider extracting the repeated function-finding logic.

The pattern fn?.type === "function" && fn?.name === functionName is repeated 6 times. Extracting this to a helper would improve maintainability.

🔎 Proposed helper extraction
+const isFunctionMatch = (fn: any, functionName: string): boolean =>
+    fn?.type === "function" && fn?.name === functionName;
+
 export function findFunctionAbiByNamespace(
     abi: Abi,
     namespace: string,
     contractName: string,
     functionName: string
 ): FunctionAbi | undefined {
     const contractPattern = `::${contractName}::`;
 
     const exactMatch = abi.find((item): item is InterfaceAbi => {
         if (item?.type !== "interface") return false;
         const name = item.name || "";
         if (!name.startsWith(`${namespace}::`)) return false;
         if (!name.includes(contractPattern)) return false;
         return (
             Array.isArray(item.items) &&
-            item.items.some(
-                (fn: any) =>
-                    fn?.type === "function" && fn?.name === functionName
-            )
+            item.items.some((fn: any) => isFunctionMatch(fn, functionName))
         );
     });
 
     if (exactMatch) {
         return exactMatch.items.find(
-            (fn: any) => fn?.type === "function" && fn?.name === functionName
+            (fn: any) => isFunctionMatch(fn, functionName)
         ) as FunctionAbi | undefined;
     }
     // ... apply similarly to other occurrences

112-129: Array arguments bypass validation.

When argsCalldata is an array (line 113-114), the arguments are used directly without validation against abiMethod.inputs. This could lead to runtime errors if the wrong number or types of arguments are provided, whereas the object format validates required fields.

Consider adding array length validation for improved developer experience:

🔎 Proposed validation for array arguments
     let args: any[];
     if (Array.isArray(argsCalldata)) {
+        const expectedCount = abiMethod.inputs.filter(
+            (input) => !isLen(input.name)
+        ).length;
+        if (argsCalldata.length !== expectedCount) {
+            throw new Error(
+                `Expected ${expectedCount} arguments for method "${method}", got ${argsCalldata.length}`
+            );
+        }
         args = argsCalldata;
     } else {
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9bf01fd and d5999c7.

📒 Files selected for processing (4)
  • packages/core/src/_test_/compile.test.ts (1 hunks)
  • packages/core/src/provider/DojoProvider.ts (8 hunks)
  • packages/core/src/utils/compile.ts (1 hunks)
  • packages/core/src/utils/index.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Apply code formatting with 4 spaces, 80 character line width, double quotes, trailing commas, and semicolons

Files:

  • packages/core/src/_test_/compile.test.ts
  • packages/core/src/utils/compile.ts
  • packages/core/src/utils/index.ts
  • packages/core/src/provider/DojoProvider.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Keep imports grouped: node/external, blank line, workspace, then relative
React hooks/components follow strict dependency arrays and early returns
Prefer guard clauses and rethrow or surface context in error handling; avoid silent catches

Files:

  • packages/core/src/_test_/compile.test.ts
  • packages/core/src/utils/compile.ts
  • packages/core/src/utils/index.ts
  • packages/core/src/provider/DojoProvider.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Maintain explicit return types for exported APIs when inference is unclear
Avoid any type; lean on generics, discriminated unions, and utility types

Files:

  • packages/core/src/_test_/compile.test.ts
  • packages/core/src/utils/compile.ts
  • packages/core/src/utils/index.ts
  • packages/core/src/provider/DojoProvider.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should remain deterministic; clean up observables/subscriptions between test cases

Files:

  • packages/core/src/_test_/compile.test.ts
🧬 Code graph analysis (4)
packages/core/src/_test_/compile.test.ts (1)
packages/core/src/utils/compile.ts (1)
  • findFunctionAbiByNamespace (22-82)
packages/core/src/utils/compile.ts (1)
packages/create-burner/src/connectors/burner.ts (1)
  • name (92-94)
packages/core/src/utils/index.ts (2)
packages/core/src/utils/compile.ts (1)
  • compileDojoCalldata (84-157)
packages/core/src/provider/DojoProvider.ts (1)
  • call (284-319)
packages/core/src/provider/DojoProvider.ts (1)
packages/core/src/utils/compile.ts (1)
  • compileDojoCalldata (84-157)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-examples
  • GitHub Check: build-packages
  • GitHub Check: build
🔇 Additional comments (7)
packages/core/src/_test_/compile.test.ts (1)

1-6: LGTM! Well-structured test setup.

The test imports and setup are clean. The as any cast on line 5 is acceptable given the JSON manifest typing limitations.

packages/core/src/provider/DojoProvider.ts (3)

300-307: LGTM! Proper calldata compilation before contract call.

The integration of compileDojoCalldata correctly passes all required parameters (ABI, namespace, contractName, entrypoint, calldata) and uses the compiled result for the contract call.


429-454: LGTM! View method implementation is consistent.

The view method correctly uses methodName for both the host property key and error messages, maintaining consistency with the disambiguation logic.


456-484: LGTM! External method implementation mirrors view method pattern.

The external (non-view) method implementation follows the same pattern as view methods with proper account validation and argument handling.

packages/core/src/utils/compile.ts (3)

14-20: LGTM! Clean helper functions.

isLen and isCairo1Type are concise, appropriately scoped helpers that encapsulate the naming conventions for Cairo length fields and type identifiers.


131-157: LGTM! Calldata array construction is well-implemented.

The reduce-based construction correctly handles Cairo length-prefixed fields and uses starknet.js's parseCalldataField for proper serialization. The __compiled__ marker is appropriately attached as a non-enumerable property to prevent serialization issues.


102-110: Verify isNoConstructorValid handles undefined abiMethod correctly.

isNoConstructorValid(method, argsCalldata, abiMethod) is called on line 102 before the null check on abiMethod at line 106. Confirm that starknet's isNoConstructorValid function safely handles undefined values, or reorder the checks.

Copy link

@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 (3)
packages/core/src/utils/compile.ts (3)

38-39: Replace any type with proper type guard.

The use of any type violates the coding guidelines. Consider using a proper type guard instead.

🔎 Proposed fix
             item.items.some(
-                (fn: any) =>
-                    fn?.type === "function" && fn?.name === functionName
+                (fn): fn is FunctionAbi =>
+                    fn?.type === "function" &&
+                    fn?.name === functionName
             )

Apply similar changes to lines 46, 58, 65, and 72.

As per coding guidelines, avoid any type and lean on generics, discriminated unions, and utility types.


22-82: Document the fallback strategy for ABI resolution.

The function implements a four-level fallback strategy (exact match → namespace match → interface scan → top-level function), but this precedence is not documented. Consider adding a JSDoc comment explaining when each strategy is used and the implications for namespace collisions.


132-148: Document the _len input skipping logic.

The logic on line 134 skips _len inputs unless they are Cairo1 types. This behavior is not immediately obvious and could benefit from a brief comment explaining why this distinction is necessary.

🔎 Proposed addition
     const callArray: string[] = abiMethod.inputs.reduce(
         (acc: string[], input: AbiEntry) => {
+            // Skip _len inputs for Cairo 0 types; Cairo 1 handles length explicitly
             if (isLen(input.name) && !isCairo1Type(input.type)) {
                 return acc;
             }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5999c7 and 08287c9.

📒 Files selected for processing (5)
  • .changeset/yellow-garlics-judge.md (1 hunks)
  • packages/core/src/_test_/compile.test.ts (1 hunks)
  • packages/core/src/provider/DojoProvider.ts (8 hunks)
  • packages/core/src/utils/compile.ts (1 hunks)
  • packages/core/src/utils/index.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/core/src/provider/DojoProvider.ts
  • packages/core/src/test/compile.test.ts
  • packages/core/src/utils/index.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Apply code formatting with 4 spaces, 80 character line width, double quotes, trailing commas, and semicolons

Files:

  • packages/core/src/utils/compile.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Keep imports grouped: node/external, blank line, workspace, then relative
React hooks/components follow strict dependency arrays and early returns
Prefer guard clauses and rethrow or surface context in error handling; avoid silent catches

Files:

  • packages/core/src/utils/compile.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Maintain explicit return types for exported APIs when inference is unclear
Avoid any type; lean on generics, discriminated unions, and utility types

Files:

  • packages/core/src/utils/compile.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-examples
  • GitHub Check: build-packages
  • GitHub Check: build
🔇 Additional comments (4)
.changeset/yellow-garlics-judge.md (1)

2-2: Verify changeset level matches feature scope.

The changeset is marked as patch level, but the description uses feat(core): which typically indicates a new feature that would warrant a minor version bump under semantic versioning. Please confirm whether this should be a minor changeset instead.

packages/core/src/utils/compile.ts (3)

1-12: LGTM!

The imports are correctly grouped and include all necessary types and utilities from starknet for ABI-based calldata compilation.


14-20: LGTM!

Both helper functions correctly identify their respective patterns: _len suffixes for length parameters and :: for Cairo 1 types.


150-154: LGTM!

The non-enumerable __compiled__ marker is a nice touch that prevents recompilation and keeps the property hidden from enumeration.

Comment on lines +112 to +129
let args: any[];
if (Array.isArray(argsCalldata)) {
args = argsCalldata;
} else {
args = abiMethod.inputs
.filter((input) => !isLen(input.name))
.map((input) => {
const value = (argsCalldata as Record<string, unknown>)[
input.name
];
if (value === undefined) {
throw new Error(
`Missing argument "${input.name}" for method "${method}"`
);
}
return value;
});
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add type validation for argsCalldata parameter.

The function assumes argsCalldata is either an array or an object, but there's no explicit validation. If it's neither (e.g., null, undefined, or a primitive), the code will fail silently or throw a cryptic error.

Additionally, args is typed as any[] which violates the coding guidelines.

🔎 Proposed fix
-    let args: any[];
+    let args: unknown[];
     if (Array.isArray(argsCalldata)) {
         args = argsCalldata;
-    } else {
+    } else if (
+        typeof argsCalldata === "object" &&
+        argsCalldata !== null
+    ) {
         args = abiMethod.inputs
             .filter((input) => !isLen(input.name))
             .map((input) => {
                 const value = (argsCalldata as Record<string, unknown>)[
                     input.name
                 ];
                 if (value === undefined) {
                     throw new Error(
                         `Missing argument "${input.name}" for method "${method}"`
                     );
                 }
                 return value;
             });
+    } else {
+        throw new Error(
+            `Invalid argsCalldata type for method "${method}". Expected array or object.`
+        );
     }

As per coding guidelines, avoid any type and lean on generics, discriminated unions, and utility types.

📝 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
let args: any[];
if (Array.isArray(argsCalldata)) {
args = argsCalldata;
} else {
args = abiMethod.inputs
.filter((input) => !isLen(input.name))
.map((input) => {
const value = (argsCalldata as Record<string, unknown>)[
input.name
];
if (value === undefined) {
throw new Error(
`Missing argument "${input.name}" for method "${method}"`
);
}
return value;
});
}
let args: unknown[];
if (Array.isArray(argsCalldata)) {
args = argsCalldata;
} else if (
typeof argsCalldata === "object" &&
argsCalldata !== null
) {
args = abiMethod.inputs
.filter((input) => !isLen(input.name))
.map((input) => {
const value = (argsCalldata as Record<string, unknown>)[
input.name
];
if (value === undefined) {
throw new Error(
`Missing argument "${input.name}" for method "${method}"`
);
}
return value;
});
} else {
throw new Error(
`Invalid argsCalldata type for method "${method}". Expected array or object.`
);
}

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