Vara.eth Contract Writer
Use this skill when writing, changing, or reviewing a Rust contract for Vara.eth with Sails.
Core Rule
Treat the target as Vara.eth, not generic Vara, generic Gear, or EVM Solidity. Use Sails with the ethexe feature and the Vara.eth artifact workflow.
Keep examples minimal, runnable, and explicit about generated artifacts. When contract behavior intersects with deployment, ABI, RPC, or runtime semantics, verify against ../../references/source-map.md.
Environment
Make sure the environment has:
- Rust toolchain.
wasm32v1-none, installed withrustup target add wasm32v1-none.sails-cli, installed withcargo install sails-cli.
Scaffold new contracts with --eth:
AUTHOR="Gear Technologies"
USERNAME="gear-tech"
cargo sails new counter \
--author "$AUTHOR" \
--username "$USERNAME" \
--eth
cd counter
Treat the generated project as the starting point. Add or revise services, events, and program state inside the generated Rust sources instead of inventing a parallel layout.
Dependencies
Vara.eth contracts must use Sails with the ethexe feature enabled.
Typical dependency block:
[dependencies]
sails-rs = { version = "1.0.0-beta.2", features = ["ethexe"] }
parity-scale-codec = { version = "3", default-features = false }
scale-info = { version = "2", default-features = false }
Keep the sails-rs version compatible with the installed sails-cli and generated project. The examples in this repository use sails-rs = "1.0.0-beta.2".
Use parity-scale-codec and scale-info when public custom types need encoding and type metadata.
Contract Shape
A clear Sails contract usually has:
#![no_std].use sails_rs::{cell::RefCell, prelude::*};.- A state/data struct that owns service state.
- An event enum marked with
#[event]. - One or more service structs that borrow shared state through
RefCell. #[service]impl blocks with exported methods.- A
#[program]impl withinitand exposed services.
For a small counter, exported methods can be add(value: u32) -> u32, sub(value: u32) -> u32, and value() -> u32, with events such as Added(u32) and Subtracted(u32). Call out underflow or overflow risks when examples mutate integers.
Services And State
Split services by responsibility, not by accident. A useful first split is user actions in one service and admin or config actions in another.
Good patterns:
- Keep shared contract data in one state struct.
- Pass
&RefCell<State>into each service. - Keep exported APIs narrow and domain-specific.
- Use helper guards such as
ensure_admin(...)orensure_not_paused(...)when several methods need the same check. - Use explicit read methods instead of assuming callers can inspect internal state.
- Emit events for meaningful state transitions that clients need to observe.
Avoid global mutable state except in the smallest teaching examples.
Errors And Results
Use #[export(unwrap_result)] for business errors that callers should see, for example:
insufficient deposited balancecaller is not admincontract is paused
Use direct panics or expect(...) only for internal failures that should be impossible in normal contract flow.
Payable Methods
For Vara.eth contracts with the ethexe feature enabled, mark a constructor or exported method as payable only when it must receive positive value with the incoming message.
Use:
#[export(payable)]
or combine it with result unwrapping:
#[export(payable, unwrap_result)]
Important points:
- Do not mark a method payable unless positive incoming value is part of its public API.
- Do not assume exported methods are payable by default.
- If value is sent to a non-payable method or constructor in the
ethexepath, execution will panic. - Do not add a redundant
value == 0guard inside a payable method;#[export(payable)]already means the method is called withvalue > 0.
Public Types
Separate two concerns:
- IDL-friendly public types.
- Solidity-ABI-friendly public types.
For Sails IDL generation, public input and output types should be encodable, introspectable, and simple enough for generated clients.
For Solidity tooling, public types need an additional check: the ABI-facing surface must be compatible with the current ethexe Solidity generation path. Run cargo sails sol --idl-path ... early when Solidity integration is planned.
Known notes for the current path:
u8does not work on the currentethexeSolidity path.i8,Vec<u8>, andStringwork on the currentethexeSolidity path.- For event fields that identify accounts, choose a type that is verified by the current
ethexebuild and Solidity generation path. In the vault example,[u8; 32]compiled cleanly for account identifiers. - If
cargo sails solfails on an IDL whose program declaration looks likeprogram <Name>.opt { ... }, regenerate the IDL withcargo sails idl --program-name <NameWithoutDot>.
Treat these as common ABI friction points until verified:
- maps such as
HashMaporBTreeMap - deeply nested enums or tuples
- heavily generic public signatures
- internal program-specific types exposed directly in public methods
For Solidity-facing APIs, be explicit about ActorId versus Ethereum address:
ActorIdis 32 bytes.- Ethereum
addressis 20 bytes. - Conversions can require padding or explicit mapping rules.
ethexe Runtime Restrictions
Design around ethexe runtime restrictions from the start.
Do not use:
- runtime randomness
- in-program creation of another program
msg_send_with_gas,msg_reply_with_gas, or other*_with_gashelpers or wrappers- gas reservation or unreservation flows
- reservation-based send or reply commit flows
- signal syscalls
- plain
wait-based blocking behavior - delayed wake flows
Flag patterns that imply these forbidden runtime syscalls:
randomcreate_programreserve_gasunreserve_gassystem_reserve_gasreply_depositreservation_send_commitreservation_reply_commitsignal_fromsignal_codegr_wait
Favor explicit message-driven state machines, deterministic logic, and externally orchestrated workflows.
Build And Test
Use:
cargo build --release
cargo test --release
Expected artifacts live under target/wasm32-gear/release/:
<program>.idl<program>.wasm<program>.opt.wasm
Before saying a contract is ready, confirm the .idl and .opt.wasm files exist.
References
Read ../../references/contract-writing-checklist.md for the final contract review checklist.
Read ../../references/source-map.md when a contract claim depends on deployment, ABI, RPC, CLI, or runtime implementation behavior.