Arithmetic and Logic
Cambrian provides arithmetic, comparison, logical, and bitwise operators. A distinguishing feature is its dual arithmetic mode: checked (default) and wrapping, giving explicit control over overflow behavior.
Arithmetic Operators
| Operator | Operation | Example |
|---|---|---|
+ | Addition | a + b |
- | Subtraction | a - b |
* | Multiplication | a * b |
/ | Division | a / b |
% | Remainder | a % b |
These operators work on all integer types (u8 through u128, i8 through
i128, U256).
let total = price * quantity;
let fee = amount * 3 / 100;
let remainder = total % batch_size;
Checked vs. Wrapping Arithmetic
By default, arithmetic in Cambrian is checked – operations that overflow or underflow will revert the transaction. This is the safe default for financial computations.
Checked Operators (Default)
Standard operators (+, -, *) perform checked arithmetic:
let sum = a + b; // reverts on overflow
let diff = a - b; // reverts if b > a for unsigned types
/ and % revert if the divisor is zero (Panic(0x12) on EVM). There is
no wrapping division.
On --target lean, the source meaning is still checked, but the default
Lean model of +/-/* wraps unless you set lean.numerics: overflow-panic.
See Checked vs Wrapping Arithmetic.
Wrapping Operators
When you intentionally want modular arithmetic (values wrap around on
overflow), use the %-suffixed operators:
let wrapped_sum = a +% b; // wraps on overflow
let wrapped_diff = a -% b; // wraps on underflow
let wrapped_prod = a *% b; // wraps on overflow
When to Use Each
| Scenario | Use |
|---|---|
| Token balances, financial math | Checked (+, -, *) |
| Hash computations, bit manipulation | Wrapping (+%, -%) |
| Counter that should wrap at max | Wrapping (+%) |
| Any case where overflow is a bug | Checked (default) |
Example showing both in the same entity:
pure fn safe_add(a: U256, b: U256) -> U256 {
a + b // checked -- reverts if overflow
}
pure fn hash_combine(a: u64, b: u64) -> u64 {
(a *% 31) +% b // wrapping -- intentional modular arithmetic
}
Comparison Operators
| Operator | Meaning | Example |
|---|---|---|
== | Equal | a == b |
!= | Not equal | a != b |
< | Less than | a < b |
<= | Less than or equal | a <= b |
> | Greater than | a > b |
>= | Greater than or equal | a >= b |
All comparison operators return bool. They are commonly used in where
clauses and if conditions:
routes {
withdraw(amount: U256)
where m_balance >= amount : throw 100
&& msg::sender == m_owner : throw 101
=> [
~> msg::sender with { value: amount }
]
}
Logical Operators
| Operator | Meaning | Example |
|---|---|---|
&& | Logical AND | a && b |
|| | Logical OR | a || b |
! | Logical NOT | !a |
Logical operators work on bool values and short-circuit: && stops at the
first false, and || stops at the first true.
macro can_withdraw(amount: U256) -> bool = {
m_balance >= amount && msg::sender == m_owner
}
Bitwise Operators
| Operator | Operation | Example |
|---|---|---|
& | Bitwise AND | a & b |
| | Bitwise OR | a | b |
^ | Bitwise XOR | a ^ b |
<< | Left shift | a << n |
>> | Right shift | a >> n |
Bitwise operators work on integer types and are useful for flag manipulation and low-level computations:
let flags = 0b1010;
let has_flag = (flags & 0b0010) != 0;
let shifted = value << 8;
Operator Precedence
From highest to lowest precedence:
| Precedence | Operators |
|---|---|
| Highest | ! (unary) |
*, /, % | |
+, - | |
<<, >> | |
& | |
^ | |
| | |
==, !=, <, <=, >, >= | |
&& | |
| Lowest | || |
Use parentheses to override precedence when the intent is not obvious:
let result = (a + b) * (c - d);
let check = (x > 0) && (y < max);