Vault Form

The Vault form defines an asset vault with three state fields — deposited (Nat per Address), total (Nat), and lock_until (Nat per Address) — and proves two invariants at compile time: deposit conservation sum(deposited) == total and time-lock enforcement requiring block.time >= lock_until before any withdrawal. Each transition (deposit, withdraw) preserves conservation by algebraic co-variance of the mapped and scalar state, while the time-lock guard is verified as a precondition on every path reaching withdrawal mutations. Both proven invariants are erased from the WASM output, producing zero-overhead verified vault contracts deployable to CosmWasm, Near, Stylus, and Polkadot.

form Vault {
  state {
    deposited  : Nat per Address
    total      : Nat
    lock_until : Nat per Address
  }

  invariant conservation :
    sum(deposited) == total

  invariant time_lock :
    withdraw => block.time >= lock_until[msg.sender]

  transition deposit(amount, lock_duration) {
    require amount > 0
    deposited[msg.sender] += amount
    total                 += amount
    lock_until[msg.sender] = block.time + lock_duration
  }
  -- conservation: sum(deposited') = sum(deposited) + amount = total + amount = total'

  transition withdraw(amount) {
    require deposited[msg.sender] >= amount
    require block.time >= lock_until[msg.sender]
    deposited[msg.sender] -= amount
    total                 -= amount
  }
  -- conservation: sum(deposited') = sum(deposited) - amount = total - amount = total'
  -- time_lock: require guard enforces block.time >= lock_until
}

State Fields

deposited : Nat per Address — maps each address to its deposited asset amount. The per keyword declares a mapping over the Address domain.

total : Nat — the total deposited scalar. Must always equal the sum of all individual deposits.

lock_until : Nat per Address — maps each address to the block timestamp after which withdrawal is permitted.

Invariant: Conservation

The conservation invariant sum(deposited) == total asserts that the aggregate of all per-address deposits equals the total variable. The compiler proves this holds for every reachable state:

Deposit: deposited[msg.sender] increases by amount, total increases by amount. Both sides of the equation increase by identical deltas. Conservation holds by algebraic co-variance.

Withdraw: deposited[msg.sender] decreases by amount, total decreases by amount. Both sides decrease equally. Conservation holds.

Invariant: Time-Lock

The time-lock invariant ensures that no withdrawal can execute before the depositor's lock period expires. The compiler verifies that every execution path reaching the withdraw state mutations passes through the require block.time >= lock_until[msg.sender] guard. Since lock_until is set during deposit as block.time + lock_duration, funds are guaranteed to remain locked for the specified duration.

Transitions

deposit(amount, lock_duration) — deposits assets and sets a time-lock. The require clause ensures a positive amount. Conservation is maintained by co-varying deposited and total by the same delta.

withdraw(amount) — withdraws assets after the lock expires. Requires sufficient deposited balance and that the current block time exceeds the lock_until timestamp. Both invariants are preserved: conservation by co-variance, time-lock by the require guard.

Frequently Asked Questions

What invariants does a Vault form prove?
The Vault form proves two invariants: deposit conservation (sum(deposited) == total) guaranteeing no assets appear or vanish through any sequence of transitions, and time-lock enforcement guaranteeing withdrawals only execute when block.time >= lock_until. Both are verified algebraically at compile time and erased from the WASM output — zero runtime overhead.
How does Formagine verify deposit conservation in the Vault?
For deposit: the compiler extracts sum(deposited') = sum(deposited) + amount and total' = total + amount, confirming both sides increase by identical deltas. For withdraw: sum(deposited') = sum(deposited) - amount and total' = total - amount. The algebraic identity sum(deposited') = total' holds in both cases by arithmetic co-variance. This proof runs at compile time and is erased — no runtime assertions needed.
How does the Vault form enforce time-lock constraints?
The withdraw transition includes require block.time >= lock_until[msg.sender] as a precondition. The compiler proves that every path reaching the withdrawal mutations passes through this guard. Since lock_until is set to block.time + lock_duration during deposit, the compiler can verify that no execution sequence can bypass the temporal constraint — the guard is structurally mandatory, not merely conventional.
What chains can the Vault form compile to?
The Vault form compiles to optimized WASM targeting CosmWasm (Cosmos ecosystem), Near Protocol, Arbitrum Stylus (Ethereum L2), and Polkadot (ink! contracts). The same form definition produces verified vault contracts for all targets with identical conservation and time-lock guarantees.

Related Forms

Resources