Skip to content

chore(cairo): bound openzeppelin_interfaces to the 2.x line - #733

Merged
tu11aa merged 1 commit into
developfrom
crew/oz-bound
Jul 30, 2026
Merged

chore(cairo): bound openzeppelin_interfaces to the 2.x line#733
tu11aa merged 1 commit into
developfrom
crew/oz-bound

Conversation

@tu11aa

@tu11aa tu11aa commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Problem

packages/snfoundry/contracts/Scarb.toml declared openzeppelin_interfaces = ">=2.0.0" — an unbounded floor. src/your_contract.cairo:12 and tests/test_contract.cairo:3 both depend on an exact module path:

use openzeppelin_interfaces::token::erc20::{IERC20Dispatcher, IERC20DispatcherTrait};

We currently resolve openzeppelin_interfaces 2.1.0, which has the ::token:: segment, so it works today. A sibling repo confirmed openzeppelin_interfaces::erc20 (without ::token::) also compiles at some versions — the path is version-dependent, not stable. If OpenZeppelin publishes a 3.x that moves or removes that segment, anyone running scarb update or regenerating a deleted Scarb.lock would silently resolve onto it and the contract would stop compiling. Our shipped Scarb.lock pins 2.1.0 (and is included in the published create-stark template), so the default path is safe today — this change closes the update path that would break it.

Change

Bound openzeppelin_interfaces to the 2.x line:

-openzeppelin_interfaces = ">=2.0.0"
+openzeppelin_interfaces = ">=2.0.0, <3.0.0"

Verified this range syntax is accepted by scarb 2.20.0 (see verification output below) — did not guess.

Only this one line in Scarb.toml changed. Scarb.lock is untouched (see verification below): 2.1.0 already satisfies the new range, so nothing re-resolves.

Verification (all run locally against scarb 2.20.0)

  1. scarb build → exit 0
   Compiling contracts v0.2.0 (packages/snfoundry/contracts/Scarb.toml)
    Finished `dev` profile target(s) in 2 seconds
EXIT_CODE=0
  1. snforge test → all tests pass (2 passed — PR test(cairo): add should_panic coverage to the contract gate #731, which adds the other 2, has not merged to develop yet; either 2 or 4 passing is expected depending on merge order)
Collected 2 test(s) from contracts package
Running 2 test(s) from tests/
[PASS] contracts_integrationtest::test_contract::test_set_greetings (...)
[PASS] contracts_integrationtest::test_contract::test_transfer (...)
Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out
EXIT_CODE=0
  1. scarb fmt --check → exit 0
EXIT_CODE=0
  1. git diff -- packages/snfoundry/contracts/Scarb.lock → empty
(no output — Scarb.lock is byte-identical to develop)

Resolved OZ versions in Scarb.lock before and after (unchanged):

openzeppelin_access      3.0.0
openzeppelin_interfaces  2.1.0
openzeppelin_introspection 3.0.0
openzeppelin_token       3.0.0
openzeppelin_utils       2.1.0

Other unbounded floors in this file (reported, not changed)

The same unbounded-floor pattern exists on the other dependencies. Listing them with whether our source has a comparable hard coupling to a specific module path, so a reviewer has the data to decide a follow-up without widening this PR:

Dependency Constraint Module-path coupling in our source?
openzeppelin_access (>=3.0.0) unbounded Yes — src/your_contract.cairo:11 imports openzeppelin_access::ownable::OwnableComponent. Same class of risk as openzeppelin_interfaces.
openzeppelin_token (>=3.0.0) unbounded No direct import found in src/your_contract.cairo or tests/test_contract.cairo — it's a transitive/declared dependency only, not referenced by an exact module path in our source. Lower risk from this specific angle.
openzeppelin_utils (>=2.0.0, dev-dependency) unbounded Yes — tests/test_contract.cairo:4 imports openzeppelin_utils::serde::SerializedAppend. Same class of risk, but scoped to dev-dependencies/tests only.
starknet (>=2.18.0) unbounded Yes — src/your_contract.cairo:13,17 import starknet::storage::{...} and starknet::{ContractAddress, get_caller_address, get_contract_address}. This is the core starknet crate itself (not OpenZeppelin), so its stability/versioning guarantees are different in kind from the OZ crates — flagging for the reviewer to weigh separately.

Not touched in this PR per scope.

Hard constraints followed

  • Only the openzeppelin_interfaces line in packages/snfoundry/contracts/Scarb.toml changed.
  • Scarb.lock is byte-identical to develop.
  • your_contract.cairo and test files untouched.
  • packages/nextjs/contracts/deployedContracts.ts untouched.

src/your_contract.cairo and tests/test_contract.cairo both depend on
the exact module path openzeppelin_interfaces::token::erc20. That
path is version-dependent (a sibling repo confirmed
openzeppelin_interfaces::erc20, without ::token::, also compiles at
some versions), so an unbounded floor (">=2.0.0") lets a future 3.x
release silently move/remove the segment and break the build on
scarb update or lockfile regen. Bound to the 2.x line so resolution
can't cross that boundary without a deliberate manifest change.
@tu11aa
tu11aa merged commit aef54d5 into develop Jul 30, 2026
2 checks passed
@tu11aa
tu11aa deleted the crew/oz-bound branch July 30, 2026 08:52
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