ITZABUILD PORTAL · v0.8 Start building ↗
GUIDELINE 07

Keep contracts deterministic

A contract should produce the same result from the supplied input and state on every node, with all effects expressed in its output.

← All guidelines

Contract rules

  • Dispatch only documented function names and reject everything else.
  • Validate authorization, required wallets, parameters, bounds, and current state before creating modifications.
  • Do not use network calls, browser state, local clocks, randomness, floating point, or environment-specific behavior.
  • Use block context supplied in ContractInput when time or ordering matters.
  • Return err(...) with safe, actionable messages; never partially apply an invalid operation.
  • Represent storage changes, files, and transfers through WalletModification.

Test logical conclusions

For every successful test, add tests for missing parameters, malformed values, unauthorized callers, duplicate actions, missing requested wallets, and old state formats.

Explicit dispatch and validation
impl ItzaContract for ExampleContract {
    fn call(&self, input: &ContractInput) -> ContractOutput {
        match input.function.as_str() {
            "save" => save(input),
            "list" => list(input),
            _ => err("unknown function"),
        }
    }
}

fn save(input: &ContractInput) -> ContractOutput {
    let value = match input.parameters.get("value") {
        Some(value) if !value.is_empty() && value.len() <= 256 => value,
        _ => return err("value must be 1-256 bytes"),
    };
    let mut output = ok();
    output.output.insert("saved".into(), value.clone());
    output
}
Bottom line

Make every dependency an explicit input and every state change an explicit output; then test rejection paths as carefully as success.