You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
src/stellar/contracts/clients/vendor-registry.client.ts is desynced from the deployed vendor-registry-contract in four independent ways, so every vendor validation call fails at runtime.
isVendorActive() invokes is_vendor_active. The contract function is is_active (contracts/vendor-registry-contract/src/lib.rs:201).
getVendor() invokes get_vendor. The contract function is get_vendor_info (line 207).
Both encode the vendor identifier with StellarSdk.nativeToScVal(vendorId, { type: 'string' }). Both contract functions take vendor: Address.
getVendor() maps active: Boolean(raw['active'] ?? raw['is_active'] ?? false). Issue [15] Add unit tests for VouchingService #7 replaced active: bool with status: VendorStatus on VendorInfo, so neither key exists and active is always false.
The failure is silent, which is what makes it dangerous. The catch block in getVendor() treats any error containing HostError or Status(ContractError as "vendor not found" and returns null. A wrong-function-name failure is indistinguishable from a genuine miss, so nothing surfaces in logs as an error and no alert fires.
For a real user: loan creation calls into vendor validation, which can never confirm a vendor is approved. Either vendors are rejected wholesale or the check degrades to a no-op depending on the caller's handling of null. The protocol loses its guarantee that funds only flow to verified learning vendors.
Ground Rules
This issue must be solved according to StepFi's established engineering standards. Before writing any code:
Read context/architecture-context.md in full
Read context/code-standards.md in full
Read context/progress-tracker.md to understand what has already been built and why, so you do not duplicate or contradict recent work
Read src/stellar/contracts/clients/reputation.client.ts to see how a working client encodes an Address argument, and follow that pattern rather than inventing one
Read src/stellar/contracts/interfaces/vendor-registry.interface.ts and src/stellar/contracts/mocks/vendor-registry.mock.ts, since both must change with the client
Read contracts/vendor-registry-contract/src/lib.rs and types.rs in the StepFi-Contracts repo to confirm the exact current signatures and the VendorStatus variants
Your PR will be rejected regardless of whether CI passes if it conflicts with anything in these files, introduces patterns inconsistent with what already exists, or duplicates functionality that was solved differently elsewhere in the codebase.
What To Build
Rename the invoked contract function in isVendorActive() from is_vendor_active to is_active.
Rename the invoked contract function in getVendor() from get_vendor to get_vendor_info.
Change argument encoding in both methods from { type: 'string' } to an Address ScVal, matching how reputation.client.ts encodes wallet addresses. Vendor identifiers reaching this client must be Stellar addresses, not database UUIDs. If any caller currently passes a UUID, that caller is also broken and must be fixed in the same PR.
Add a VendorStatus union type to src/stellar/contracts/interfaces/vendor-registry.interface.ts with the four contract variants: Pending, Approved, Suspended, Rejected.
Replace active: boolean on the VendorInfo interface with status: VendorStatus. Do not keep both. A derived active field invites callers to keep reading the stale one.
Update the mapping in getVendor() to read status from the decoded value and validate it against the four known variants. An unrecognized value must throw, not silently default to a passing state.
Narrow the catch block in getVendor(). Return null only for a genuine "vendor not found" contract error. Any other HostError must be logged at error level and rethrown as ContractReadError, so a future function-name drift fails loudly instead of masquerading as a missing vendor.
Update every call site of isVendorActive() and getVendor() for the new status shape.
Update src/stellar/contracts/mocks/vendor-registry.mock.ts to match the new interface, and make the mock return status rather than active.
Add a unit test that would have caught this: assert the exact contract function name passed to simulateContractCall. A test that only mocks the client cannot detect a wrong function name, which is why this shipped.
isVendorActive() invokes is_active and getVendor() invokes get_vendor_info
Both encode the vendor identifier as an Address ScVal
VendorInfo exposes status: VendorStatus and no active boolean
An unrecognized status value throws rather than defaulting to a passing state
A non-"not found" contract error is logged and rethrown, not swallowed into null
A test asserts the exact contract function name and argument type passed to simulateContractCall
All existing vendor call sites compile and pass against the new shape
All state-changing operations validated and safe against invalid input
Mandatory Checks Before Opening PR
All context/ files read and understood
Code follows context/code-standards.md exactly
context/progress-tracker.md updated with an accurate entry describing the change and why
Build passes with zero errors (npm run build)
All existing tests still pass, test count has not decreased
New tests written covering the new functionality
No new any types
Full Swagger decorators on any new or changed endpoint
PR template filled out completely, including the "problem this solves" and "how it was tested" sections
PR references this issue number exactly
PRs that fail any check above will be closed without review, regardless of how much work went into them. No exceptions. This is a Grantfox-funded, quality-first project. Code that does not meet these standards will not be merged and will not be paid.
Problem
src/stellar/contracts/clients/vendor-registry.client.tsis desynced from the deployedvendor-registry-contractin four independent ways, so every vendor validation call fails at runtime.isVendorActive()invokesis_vendor_active. The contract function isis_active(contracts/vendor-registry-contract/src/lib.rs:201).getVendor()invokesget_vendor. The contract function isget_vendor_info(line 207).StellarSdk.nativeToScVal(vendorId, { type: 'string' }). Both contract functions takevendor: Address.getVendor()mapsactive: Boolean(raw['active'] ?? raw['is_active'] ?? false). Issue [15] Add unit tests forVouchingService#7 replacedactive: boolwithstatus: VendorStatusonVendorInfo, so neither key exists andactiveis alwaysfalse.The failure is silent, which is what makes it dangerous. The
catchblock ingetVendor()treats any error containingHostErrororStatus(ContractErroras "vendor not found" and returnsnull. A wrong-function-name failure is indistinguishable from a genuine miss, so nothing surfaces in logs as an error and no alert fires.For a real user: loan creation calls into vendor validation, which can never confirm a vendor is approved. Either vendors are rejected wholesale or the check degrades to a no-op depending on the caller's handling of
null. The protocol loses its guarantee that funds only flow to verified learning vendors.Ground Rules
This issue must be solved according to StepFi's established engineering standards. Before writing any code:
src/stellar/contracts/clients/reputation.client.tsto see how a working client encodes anAddressargument, and follow that pattern rather than inventing onesrc/stellar/contracts/interfaces/vendor-registry.interface.tsandsrc/stellar/contracts/mocks/vendor-registry.mock.ts, since both must change with the clientcontracts/vendor-registry-contract/src/lib.rsandtypes.rsin the StepFi-Contracts repo to confirm the exact current signatures and theVendorStatusvariantsYour PR will be rejected regardless of whether CI passes if it conflicts with anything in these files, introduces patterns inconsistent with what already exists, or duplicates functionality that was solved differently elsewhere in the codebase.
What To Build
isVendorActive()fromis_vendor_activetois_active.getVendor()fromget_vendortoget_vendor_info.{ type: 'string' }to anAddressScVal, matching howreputation.client.tsencodes wallet addresses. Vendor identifiers reaching this client must be Stellar addresses, not database UUIDs. If any caller currently passes a UUID, that caller is also broken and must be fixed in the same PR.VendorStatusunion type tosrc/stellar/contracts/interfaces/vendor-registry.interface.tswith the four contract variants:Pending,Approved,Suspended,Rejected.active: booleanon theVendorInfointerface withstatus: VendorStatus. Do not keep both. A derivedactivefield invites callers to keep reading the stale one.getVendor()to readstatusfrom the decoded value and validate it against the four known variants. An unrecognized value must throw, not silently default to a passing state.catchblock ingetVendor(). Returnnullonly for a genuine "vendor not found" contract error. Any otherHostErrormust be logged at error level and rethrown asContractReadError, so a future function-name drift fails loudly instead of masquerading as a missing vendor.isVendorActive()andgetVendor()for the newstatusshape.src/stellar/contracts/mocks/vendor-registry.mock.tsto match the new interface, and make the mock returnstatusrather thanactive.simulateContractCall. A test that only mocks the client cannot detect a wrong function name, which is why this shipped.Files To Touch
src/stellar/contracts/clients/vendor-registry.client.tssrc/stellar/contracts/interfaces/vendor-registry.interface.tssrc/stellar/contracts/mocks/vendor-registry.mock.tssrc/modules/vendors/vendors.service.ts(call sites)src/modules/loans/loans.service.ts(call sites, if vendor validation runs there)src/stellar/contracts/clients/vendor-registry.client.spec.ts(new)Acceptance Criteria
isVendorActive()invokesis_activeandgetVendor()invokesget_vendor_infoAddressScValVendorInfoexposesstatus: VendorStatusand noactivebooleannullsimulateContractCallMandatory Checks Before Opening PR
npm run build)anytypesPRs that fail any check above will be closed without review, regardless of how much work went into them. No exceptions. This is a Grantfox-funded, quality-first project. Code that does not meet these standards will not be merged and will not be paid.