-
Notifications
You must be signed in to change notification settings - Fork 54
feat(core): improve provider and internalize calldata compiler #514
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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.
getContractByNameusesArray.find()which returnsundefinedif no match is found. If the contract doesn't exist in the manifest, accessingcontract.addressandcontract.abion 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 forcompileDojoCalldata.The test suite covers
findFunctionAbiByNamespacewell with multiple scenarios including the fallback behavior and nonexistent function case. However,compileDojoCalldatais 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_systemNamenamespace="foo",contractName="bar_baz"→foo_bar_baz_systemNameThis 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 === functionNameis 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
argsCalldatais an array (line 113-114), the arguments are used directly without validation againstabiMethod.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
📒 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.tspackages/core/src/utils/compile.tspackages/core/src/utils/index.tspackages/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.tspackages/core/src/utils/compile.tspackages/core/src/utils/index.tspackages/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
Avoidanytype; lean on generics, discriminated unions, and utility types
Files:
packages/core/src/_test_/compile.test.tspackages/core/src/utils/compile.tspackages/core/src/utils/index.tspackages/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 anycast 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
compileDojoCalldatacorrectly 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
methodNamefor 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.
isLenandisCairo1Typeare 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
parseCalldataFieldfor proper serialization. The__compiled__marker is appropriately attached as a non-enumerable property to prevent serialization issues.
102-110: VerifyisNoConstructorValidhandles undefinedabiMethodcorrectly.
isNoConstructorValid(method, argsCalldata, abiMethod)is called on line 102 before the null check onabiMethodat line 106. Confirm that starknet'sisNoConstructorValidfunction safely handles undefined values, or reorder the checks.
d5999c7 to
08287c9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/core/src/utils/compile.ts (3)
38-39: Replaceanytype with proper type guard.The use of
anytype 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
anytype 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_leninput skipping logic.The logic on line 134 skips
_leninputs 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
📒 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
Avoidanytype; 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
patchlevel, but the description usesfeat(core):which typically indicates a new feature that would warrant aminorversion bump under semantic versioning. Please confirm whether this should be aminorchangeset 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:
_lensuffixes 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.
| 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; | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
| 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.` | |
| ); | |
| } |
Closes #
Introduced changes
Checklist
Summary by CodeRabbit
Tests
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.