Summary
Before a contract graph is compiled or deployed, it should be validated across five distinct layers: structural integrity, field completeness, Stellar address format, cross-block semantic consistency, and circular dependency detection. The result must be surfaced as structured, per-node errors on the canvas and must gate the Compile, Simulate, and Deploy buttons.
This is significantly more complex than it first appears — it requires a full multi-pass validation pipeline that understands the semantics of each block type, propagates errors to the correct canvas nodes, and integrates with the existing validateContractGraph / validateGraphStructure stack already in src/lib/compile/validate.ts.
Background
A basic structural validator (validateGraphStructure) already exists and checks reachability and executable-block presence. This issue extends it into a deep semantic validator that catches errors the current compiler only discovers at cargo build time — or never, silently producing broken contracts.
Required Validation Passes
Pass 1 — Structural (extend existing)
- All nodes reachable from the single
default (Start) node.
- Exactly one Start node exists.
- No duplicate node IDs or edge IDs.
Pass 2 — Field completeness per block type
| Block |
Required fields |
Error code |
Transfer |
params.token or params.asset.contractId non-empty |
MISSING_TOKEN |
Storage |
params.storageKey non-empty and ≤ 9 chars (Soroban symbol limit) |
MISSING_STORAGE_KEY / STORAGE_KEY_TOO_LONG |
Event |
params.eventName non-empty |
MISSING_EVENT_NAME |
Condition |
params.conditionExpression present and complete (both operands non-empty) |
INCOMPLETE_EXPRESSION |
Pass 3 — Stellar address format
- All fields that accept an
Address (token contract ID, asset contractId) must either be empty or match the Stellar strkey pattern: 56 characters starting with C (contract) or G (account).
- Error code:
INVALID_STELLAR_ADDRESS
- Error must identify both the node ID and the specific field name.
Pass 4 — Semantic cross-block rules
- An
Auth block must appear before any Transfer block in the topological execution order (auth-before-transfer rule). Violation → AUTH_AFTER_TRANSFER.
- A
Condition block with a storageKey operand must have a Storage (write) block for that key reachable from Start in the same graph. Violation → UNRESOLVED_STORAGE_REF.
- A
Condition block using an invocationArg operand must reference a name that exists in the deriveParams output for the current graph. Violation → UNRESOLVED_ARG_REF.
Pass 5 — Cycle detection (extend existing)
- Detect any directed cycle in the graph using DFS.
- Report the cycle node IDs in the error
details array.
- Error code:
GRAPH_CYCLE_DETECTED
Validation Result Type
export interface NodeValidationError {
nodeId: string
code: string
message: string
field?: string // e.g. "params.storageKey"
}
export interface GraphValidationResult {
valid: boolean
nodeErrors: NodeValidationError[] // per-node, for canvas highlighting
graphErrors: CompileError[] // graph-level (cycle, missing start, etc.)
}
export function validateGraphDeep(graph: ContractGraph): GraphValidationResult
Canvas Integration (BlockEditor.tsx)
- Run
validateGraphDeep debounced (300 ms) on every nodes/edges change.
- Pass
nodeErrors down to BlockNode via node data; nodes with errors render a red border and a warning icon badge showing the error count.
- Hovering the warning badge shows a tooltip with all error messages for that node.
- The Compile, Simulate, and Deploy buttons are disabled and show a red badge with the total error count while
!result.valid.
Acceptance Criteria
Summary
Before a contract graph is compiled or deployed, it should be validated across five distinct layers: structural integrity, field completeness, Stellar address format, cross-block semantic consistency, and circular dependency detection. The result must be surfaced as structured, per-node errors on the canvas and must gate the Compile, Simulate, and Deploy buttons.
This is significantly more complex than it first appears — it requires a full multi-pass validation pipeline that understands the semantics of each block type, propagates errors to the correct canvas nodes, and integrates with the existing
validateContractGraph/validateGraphStructurestack already insrc/lib/compile/validate.ts.Background
A basic structural validator (
validateGraphStructure) already exists and checks reachability and executable-block presence. This issue extends it into a deep semantic validator that catches errors the current compiler only discovers atcargo buildtime — or never, silently producing broken contracts.Required Validation Passes
Pass 1 — Structural (extend existing)
default(Start) node.Pass 2 — Field completeness per block type
Transferparams.tokenorparams.asset.contractIdnon-emptyMISSING_TOKENStorageparams.storageKeynon-empty and ≤ 9 chars (Soroban symbol limit)MISSING_STORAGE_KEY/STORAGE_KEY_TOO_LONGEventparams.eventNamenon-emptyMISSING_EVENT_NAMEConditionparams.conditionExpressionpresent and complete (both operands non-empty)INCOMPLETE_EXPRESSIONPass 3 — Stellar address format
Address(token contract ID, asset contractId) must either be empty or match the Stellar strkey pattern: 56 characters starting withC(contract) orG(account).INVALID_STELLAR_ADDRESSPass 4 — Semantic cross-block rules
Authblock must appear before anyTransferblock in the topological execution order (auth-before-transfer rule). Violation →AUTH_AFTER_TRANSFER.Conditionblock with astorageKeyoperand must have aStorage(write) block for that key reachable from Start in the same graph. Violation →UNRESOLVED_STORAGE_REF.Conditionblock using aninvocationArgoperand must reference a name that exists in thederiveParamsoutput for the current graph. Violation →UNRESOLVED_ARG_REF.Pass 5 — Cycle detection (extend existing)
detailsarray.GRAPH_CYCLE_DETECTEDValidation Result Type
Canvas Integration (
BlockEditor.tsx)validateGraphDeepdebounced (300 ms) on everynodes/edgeschange.nodeErrorsdown toBlockNodevia node data; nodes with errors render a red border and a warning icon badge showing the error count.!result.valid.Acceptance Criteria
validateGraphDeepis implemented and exported fromsrc/lib/compile/validate.ts.Transfernode with an emptytokenfield fails withMISSING_TOKEN.Storagenode with a key longer than 9 characters fails withSTORAGE_KEY_TOO_LONG.INVALID_STELLAR_ADDRESSand identifies the field name.Authblock placed after aTransferin execution order fails withAUTH_AFTER_TRANSFER.GRAPH_CYCLE_DETECTEDand lists the involved node IDs.validate.test.tscover all five passes with at least two cases each (valid and invalid).Storageblock with a 10-character key, asserts the node turns red, fixes the key, asserts the red border disappears.