Skip to content
LogoLogo

Guards

Solidity uses modifiers like onlyOwner, applied one function at a time. In Lithe, you group entrypoints under a dispatcher and write the guard once for the group, and the ABI stays flat.

guards.lithe
(* Solidity writes `onlyOwner` modifiers. Lithe groups guarded actions under one
   dispatcher. The guard runs in the dispatcher's body, before `with case`, and
   every case under it inherits it. The ABI still gets one function per case:
   `setFee(uint256)`, `pause()`, `deposit(uint256)`. *)
 
import "std/evm";
 
error NotOwner;
error Paused;
 
storage is record {
  owner : address;
  paused : bool;
  fee : uint;
  deposits : map(address, uint);
}
 
type OwnerAction is
  | SetFee external setFee of (fee : uint)
  | Pause external pause
 
type UserAction is
  | Deposit external deposit of (amount : uint)
 
type Action is
  | Owner of OwnerAction
  | User of UserAction
 
function ownerMain(const action : OwnerAction)
  raises NotOwner
is {
  assert(Evm.sender() = storage.owner, NotOwner);
}
with case action of {
| SetFee(fee) -> { storage.fee := fee; }
| Pause -> { storage.paused := true; }
}
 
function userMain(const action : UserAction)
  raises Paused
is {
  assert(not storage.paused, Paused);
}
with case action of {
| Deposit(amount) -> {
    storage.deposits[Evm.sender()] := storage.deposits[Evm.sender()] + amount;
  }
}
 
external function main(const action : Action)
  raises NotOwner | Paused
is
  case action of {
  | Owner(a) -> ownerMain(a)
  | User(a) -> userMain(a)
  }
 
(* A guard can also be a plain internal function called first. *)
view function onlyOwner() raises NotOwner is {
  assert(Evm.sender() = storage.owner, NotOwner);
}
 
external function setOwner(const owner : address)
  raises NotOwner
is {
  onlyOwner();
  storage.owner := owner;
}
  • setFee and pause inherit the owner check, and deposit inherits the pause check. A new owner action added to OwnerAction gets the guard automatically.
  • lithe abi-tree guards.lithe prints each entrypoint's guards.
  • A plain internal function called first, like onlyOwner, works too. See Entrypoint dispatch.
  • A module's externals that need the contract's check name a module guard, which the contract writes. See Guards.