Summary
Every contract's error codes are dense, low, and mutually colliding: all six enums start at 1 — NotAdmin = 1 (contracts/creditline-contract/src/errors.rs:8, contracts/liquidity-pool-contract/src/errors.rs:7, contracts/reputation-contract/src/errors.rs:8, contracts/vouching-contract/src/errors.rs:3 via NotInitialized = 1), AlreadyInitialized = 1 (contracts/parameters-contract/src/errors.rs:7, contracts/vendor-registry-contract/src/errors.rs:7) — so the integer 1 means "NotAdmin" in creditline, "AlreadyInitialized" in parameters, "NotInitialized" in vouching, and "AlreadyInitialized" again in vendor-registry, with no way for a reader, log, or client to attribute a bare code to its contract. Variant names collide too: NotAdmin appears in five enums, NotInitialized in all six, Overflow in all six, Underflow in all six, ReentrancyDetected in all six. On top of that the vendor-registry type is named the bare pub enum Error (contracts/vendor-registry-contract/src/errors.rs:6), aliased to VendorRegistryError only at the re-export (contracts/vendor-registry-contract/src/lib.rs:17) while internal code still writes Error::… (contracts/vendor-registry-contract/src/lib.rs:33,43,61,67,71), and contractimport!-generated clients surface the raw enum name. A code-number collision makes cross-contract failures ambiguous exactly where try-calls swallow foreign errors (contracts/vouching-contract/src/lib.rs:205,225,238; contracts/creditline-contract/src/lib.rs:355,663,1177).
Why these together: both workstreams are faces of one property — one error integer decoded anywhere unambiguously identifies its contract and variant, and the enum that encodes vendor-registry failures carries its contract's name instead of the bare, collision-prone Error. Namespaced ranges without a renamed vendor enum still leave the one bare Error in the workspace; a rename without ranges still lets 1 mean four different things.
Labels
area: contracts type: refactor priority: medium
Workstream 1 — Assign per-contract code ranges
Objective
Give each contract a disjoint band of error codes so a single integer identifies both the contract and the variant, without changing any variant's local meaning.
Problem
All six enums begin at 1 and run densely (creditline 1-33, liquidity-pool 1-19, parameters 1-19, reputation 1-11, vouching 1-13, vendor-registry 1-13), so codes overlap completely across contracts. Cross-contract clients decode foreign errors by value; with ranges unassigned, 1 from vouching and 1 from creditline are indistinguishable in a shared log or in the try_invoke_contract error path (vouching-contract/src/lib.rs:205,225,238; creditline-contract/src/lib.rs:663,1177).
Scope
contracts/creditline-contract/src/errors.rs (edit — rebase codes into its band)
contracts/liquidity-pool-contract/src/errors.rs (edit)
contracts/parameters-contract/src/errors.rs (edit)
contracts/reputation-contract/src/errors.rs (edit)
contracts/vouching-contract/src/errors.rs (edit)
contracts/vendor-registry-contract/src/errors.rs (edit)
context/progress-tracker.md (edit — record the reserved bands)
Implementation
- Reserve 100-wide bands by contract, in workspace-member order (
Cargo.toml:members): creditline 100-199, reputation 200-299, parameters 300-399, vendor-registry 400-499, liquidity-pool 500-599, vouching 600-699. Base offset knob per contract: ERROR_BASE (constant, default = the band start) so codes read 100 + local.
- Rewrite each
Variant = n to n = base + local keeping current local order and gaps reserved for growth; keep #[repr(u32)] (errors.rs line 6 of each) unchanged so the wire type stays u32.
- Because codes are ABI-visible, treat this as a breaking code change: bump nothing publicly until the catalog regeneration lands; add a
context/progress-tracker.md note recording each band so future contracts extend rather than reuse.
- Add a workspace test (one per contract or a shared
errors_bands test) asserting every variant's code falls inside its contract's reserved band and that no two bands overlap.
Acceptance Criteria
Testing
- Unit: per-contract band-membership test asserting
min(types) >= base && max(types) < base + 100.
- Integration: an
errors_bands integration test across the workspace asserting the six bands are disjoint.
Workstream 2 — Rename the bare Error enum to VendorRegistryError
Objective
Replace the workspace's only bare Error enum with a contract-named type everywhere it is referenced, so the enum name itself is unambiguous.
Problem
contracts/vendor-registry-contract/src/errors.rs:6 declares pub enum Error; the crate re-exports it under an alias (pub use errors::Error as VendorRegistryError; at vendor-registry-contract/src/lib.rs:17) while importing the bare name for internal use (use errors::Error; at :12) and calling Error::AlreadyInitialized etc. throughout (:33,43,61,67,71,95,104,118). A use ...::Error in any consumer silently shadows the prelude/host Error, and contractimport! exposes the generated client's error type under the bare name.
Scope
contracts/vendor-registry-contract/src/errors.rs (edit — rename Error → VendorRegistryError)
contracts/vendor-registry-contract/src/lib.rs (edit — drop the alias dance, use the new name)
contracts/vendor-registry-contract/src/tests.rs (edit — update references)
contracts/vendor-registry-contract/src/storage.rs, events.rs, access.rs (edit — any Error:: references)
Implementation
- Rename the enum declaration to
pub enum VendorRegistryError.
- Replace all
Error:: references across the crate with VendorRegistryError::; remove the use errors::Error; import (lib.rs:12) and the pub use errors::Error as VendorRegistryError; alias (:17) in favour of a single pub use errors::VendorRegistryError;.
- Grep gate:
contracts/vendor-registry-contract/src must contain zero occurrences of a bare Error:: or use ...::Error; after the change; add it to the PR checklist.
Acceptance Criteria
Testing
- Unit: existing vendor-registry test suite (
contracts/vendor-registry-contract/src/tests.rs, 26 tests) compiles and passes against the renamed enum.
- Integration:
cargo clippy --workspace --all-targets --locked -- -D warnings is clean (no leftover bare Error).
Shared acceptance criteria (whole epic)
Security & compatibility considerations
- Error codes are ABI-visible and appear in
try_* return payloads (vouching-contract/src/lib.rs:205,225,238, creditline-contract/src/lib.rs:663,1177); renaming the codes is a coordinated, breaking change — land it together with the catalog regeneration so clients and docs move at once.
- Band reservation must be recorded so a later contract cannot be assigned an overlapping range.
contractimport!-generated clients for these WASMs must be rebuilt in CI (contracts-ci.yml builds the dependency WASMs before creditline) so the renamed/rebanded types propagate.
References
contracts/creditline-contract/src/errors.rs:7-41 — 33-variant CreditLineError; NotAdmin = 1 at :8.
contracts/liquidity-pool-contract/src/errors.rs:6-26 — 19-variant LiquidityPoolError; NotAdmin = 1 at :7.
contracts/parameters-contract/src/errors.rs:6-26 — 19-variant ParametersError; AlreadyInitialized = 1 at :7.
contracts/reputation-contract/src/errors.rs:7-19 — 11-variant ReputationError; NotAdmin = 1 at :8.
contracts/vouching-contract/src/errors.rs:6-20 — 13-variant VouchingError; NotInitialized = 1 at :7.
contracts/vendor-registry-contract/src/errors.rs:6-20 — 13-variant bare Error.
contracts/vendor-registry-contract/src/lib.rs:6,12,17,33,43,61,67,71,95,104,118 — alias/import/Error:: call sites.
contracts/vouching-contract/src/lib.rs:205,225,238 — cross-contract try-calls surfacing foreign errors.
contracts/creditline-contract/src/lib.rs:355,663,1177 — creditline try-calls to vendor-registry/reputation.
Cargo.toml:members — contract order that fixes band assignment.
context/progress-tracker.md — place to record reserved bands.
contracts/creditline-contract/src/lib.rs:9,14-19 — contractimport! clients that expose raw enum names.
- Feeds the generated error reference; the per-contract error-model documentation work fills each band's rows.
If you're solving this with AI
In scope: the six contracts/*-contract/src/errors.rs files and the call sites that reference their variants (lib.rs, storage.rs, events.rs, access.rs, tests.rs within those crates), plus context/progress-tracker.md — only.
Out of scope: other repos; changing variant names (only the vendor-registry enum name and the numeric codes change); adding/removing variants; renaming public contract functions; new dependencies; reformatting untouched files.
Must: follow the repo PR template exactly; keep CI green for real (build + fmt + clippy + tests); add the band-membership and disjointness tests; a regression test that FAILS if a code escapes its band; reference this issue (Closes #<n>); no secrets.
Contribution requirements: Follow the repo PR template exactly — https://github.com/StepFi-app/StepFi-Contracts/blob/main/.github/pull_request_template.md. Reference this issue in your PR. CI must pass green (build + fmt + clippy + tests). No secrets; no out-of-scope changes.
Summary
Every contract's error codes are dense, low, and mutually colliding: all six enums start at 1 —
NotAdmin = 1(contracts/creditline-contract/src/errors.rs:8,contracts/liquidity-pool-contract/src/errors.rs:7,contracts/reputation-contract/src/errors.rs:8,contracts/vouching-contract/src/errors.rs:3viaNotInitialized = 1),AlreadyInitialized = 1(contracts/parameters-contract/src/errors.rs:7,contracts/vendor-registry-contract/src/errors.rs:7) — so the integer1means "NotAdmin" in creditline, "AlreadyInitialized" in parameters, "NotInitialized" in vouching, and "AlreadyInitialized" again in vendor-registry, with no way for a reader, log, or client to attribute a bare code to its contract. Variant names collide too:NotAdminappears in five enums,NotInitializedin all six,Overflowin all six,Underflowin all six,ReentrancyDetectedin all six. On top of that the vendor-registry type is named the barepub enum Error(contracts/vendor-registry-contract/src/errors.rs:6), aliased toVendorRegistryErroronly at the re-export (contracts/vendor-registry-contract/src/lib.rs:17) while internal code still writesError::…(contracts/vendor-registry-contract/src/lib.rs:33,43,61,67,71), andcontractimport!-generated clients surface the raw enum name. A code-number collision makes cross-contract failures ambiguous exactly where try-calls swallow foreign errors (contracts/vouching-contract/src/lib.rs:205,225,238;contracts/creditline-contract/src/lib.rs:355,663,1177).Why these together: both workstreams are faces of one property — one error integer decoded anywhere unambiguously identifies its contract and variant, and the enum that encodes vendor-registry failures carries its contract's name instead of the bare, collision-prone
Error. Namespaced ranges without a renamed vendor enum still leave the one bareErrorin the workspace; a rename without ranges still lets1mean four different things.Labels
area: contractstype: refactorpriority: mediumWorkstream 1 — Assign per-contract code ranges
Objective
Give each contract a disjoint band of error codes so a single integer identifies both the contract and the variant, without changing any variant's local meaning.
Problem
All six enums begin at 1 and run densely (
creditline 1-33,liquidity-pool 1-19,parameters 1-19,reputation 1-11,vouching 1-13,vendor-registry 1-13), so codes overlap completely across contracts. Cross-contract clients decode foreign errors by value; with ranges unassigned,1from vouching and1from creditline are indistinguishable in a shared log or in thetry_invoke_contracterror path (vouching-contract/src/lib.rs:205,225,238;creditline-contract/src/lib.rs:663,1177).Scope
contracts/creditline-contract/src/errors.rs(edit — rebase codes into its band)contracts/liquidity-pool-contract/src/errors.rs(edit)contracts/parameters-contract/src/errors.rs(edit)contracts/reputation-contract/src/errors.rs(edit)contracts/vouching-contract/src/errors.rs(edit)contracts/vendor-registry-contract/src/errors.rs(edit)context/progress-tracker.md(edit — record the reserved bands)Implementation
Cargo.toml:members): creditline100-199, reputation200-299, parameters300-399, vendor-registry400-499, liquidity-pool500-599, vouching600-699. Base offset knob per contract:ERROR_BASE(constant, default = the band start) so codes read100 + local.Variant = nton = base + localkeeping current local order and gaps reserved for growth; keep#[repr(u32)](errors.rsline 6 of each) unchanged so the wire type staysu32.context/progress-tracker.mdnote recording each band so future contracts extend rather than reuse.errors_bandstest) asserting every variant's code falls inside its contract's reserved band and that no two bands overlap.Acceptance Criteria
ParametersError::NotAdminto101→ test fails).#[repr(u32)]and variant order semantics are preserved; only numeric values change.Testing
min(types) >= base && max(types) < base + 100.errors_bandsintegration test across the workspace asserting the six bands are disjoint.Workstream 2 — Rename the bare
Errorenum toVendorRegistryErrorObjective
Replace the workspace's only bare
Errorenum with a contract-named type everywhere it is referenced, so the enum name itself is unambiguous.Problem
contracts/vendor-registry-contract/src/errors.rs:6declarespub enum Error; the crate re-exports it under an alias (pub use errors::Error as VendorRegistryError;atvendor-registry-contract/src/lib.rs:17) while importing the bare name for internal use (use errors::Error;at:12) and callingError::AlreadyInitializedetc. throughout (:33,43,61,67,71,95,104,118). Ause ...::Errorin any consumer silently shadows the prelude/hostError, andcontractimport!exposes the generated client's error type under the bare name.Scope
contracts/vendor-registry-contract/src/errors.rs(edit — renameError→VendorRegistryError)contracts/vendor-registry-contract/src/lib.rs(edit — drop the alias dance, use the new name)contracts/vendor-registry-contract/src/tests.rs(edit — update references)contracts/vendor-registry-contract/src/storage.rs,events.rs,access.rs(edit — anyError::references)Implementation
pub enum VendorRegistryError.Error::references across the crate withVendorRegistryError::; remove theuse errors::Error;import (lib.rs:12) and thepub use errors::Error as VendorRegistryError;alias (:17) in favour of a singlepub use errors::VendorRegistryError;.contracts/vendor-registry-contract/srcmust contain zero occurrences of a bareError::oruse ...::Error;after the change; add it to the PR checklist.Acceptance Criteria
grep -rn "\bError\b" contracts/vendor-registry-contract/src | grep -v VendorRegistryErrorreturns nothing.VendorRegistryError.Testing
contracts/vendor-registry-contract/src/tests.rs, 26 tests) compiles and passes against the renamed enum.cargo clippy --workspace --all-targets --locked -- -D warningsis clean (no leftover bareError).Shared acceptance criteria (whole epic)
cargo fmt --all --check,cargo clippy --workspace --all-targets --locked -- -D warnings,cargo build --locked --target wasm32-unknown-unknown --release, andcargo test --lockedall exit 0; test count does not drop.context/progress-tracker.md(bands) and flow into the generateddocs/ERROR_CODES.md.#[repr(u32)]preserved.errors.rsfiles and their referencing call sites.Security & compatibility considerations
try_*return payloads (vouching-contract/src/lib.rs:205,225,238,creditline-contract/src/lib.rs:663,1177); renaming the codes is a coordinated, breaking change — land it together with the catalog regeneration so clients and docs move at once.contractimport!-generated clients for these WASMs must be rebuilt in CI (contracts-ci.ymlbuilds the dependency WASMs before creditline) so the renamed/rebanded types propagate.References
contracts/creditline-contract/src/errors.rs:7-41— 33-variantCreditLineError;NotAdmin = 1at:8.contracts/liquidity-pool-contract/src/errors.rs:6-26— 19-variantLiquidityPoolError;NotAdmin = 1at:7.contracts/parameters-contract/src/errors.rs:6-26— 19-variantParametersError;AlreadyInitialized = 1at:7.contracts/reputation-contract/src/errors.rs:7-19— 11-variantReputationError;NotAdmin = 1at:8.contracts/vouching-contract/src/errors.rs:6-20— 13-variantVouchingError;NotInitialized = 1at:7.contracts/vendor-registry-contract/src/errors.rs:6-20— 13-variant bareError.contracts/vendor-registry-contract/src/lib.rs:6,12,17,33,43,61,67,71,95,104,118— alias/import/Error::call sites.contracts/vouching-contract/src/lib.rs:205,225,238— cross-contract try-calls surfacing foreign errors.contracts/creditline-contract/src/lib.rs:355,663,1177— creditline try-calls to vendor-registry/reputation.Cargo.toml:members— contract order that fixes band assignment.context/progress-tracker.md— place to record reserved bands.contracts/creditline-contract/src/lib.rs:9,14-19—contractimport!clients that expose raw enum names.If you're solving this with AI
In scope: the six
contracts/*-contract/src/errors.rsfiles and the call sites that reference their variants (lib.rs,storage.rs,events.rs,access.rs,tests.rswithin those crates), pluscontext/progress-tracker.md— only.Out of scope: other repos; changing variant names (only the vendor-registry enum name and the numeric codes change); adding/removing variants; renaming public contract functions; new dependencies; reformatting untouched files.
Must: follow the repo PR template exactly; keep CI green for real (build + fmt + clippy + tests); add the band-membership and disjointness tests; a regression test that FAILS if a code escapes its band; reference this issue (
Closes #<n>); no secrets.Contribution requirements: Follow the repo PR template exactly — https://github.com/StepFi-app/StepFi-Contracts/blob/main/.github/pull_request_template.md. Reference this issue in your PR. CI must pass green (build + fmt + clippy + tests). No secrets; no out-of-scope changes.