Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion docs/builder/smart-contracts/patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,29 @@ let elapsed = current_block - last_block;

For Felt arithmetic, values wrap modulo the prime field (no overflow panic), but the result may not be what you expect if you're treating Felts as integers. See [Types — Felt](./types#felt--field-elements) for details.

### When to use Felt vs u64

Use `Felt` when you need field-native behavior that must match the Miden VM exactly:

- Hashing and commitment inputs
- Storage words and protocol-defined field values
- Arithmetic that is intentionally part of a field-based construction

Use `u64` when you are working with business quantities and expect integer semantics:

- Token amounts and balances
- Fee calculations and proportional splits
- Counters, limits, cooldowns, and similar control-flow values

The `as_u64()` conversion is zero-cost and gives you standard Rust integer behavior. For DeFi-style
logic, convert out of `Felt`, do the arithmetic in `u64`, and convert back with
`Felt::from_u64_unchecked()` once you know the result is in range.

### Anti-patterns

- **Don't store secrets in contract code** — contract code is visible on-chain
- **Don't skip nonce management** — prevents replay attacks
- **Be careful with Felt division** — Felt division computes the multiplicative inverse, not integer division. Convert to `u64` first for integer-style operations
- **Don't use Felt division for business logic** — convert to `u64` first when you need integer-style division or rounding

## `#![no_std]` environment

Expand Down
19 changes: 19 additions & 0 deletions docs/builder/smart-contracts/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,25 @@ x += felt!(1); // x is now felt!(6)
x *= felt!(2); // x is now felt!(12)
```

### Division

`Felt` division only matches standard integer division when the result is exact. For `10 / 2`, both
`u64` arithmetic and `Felt` arithmetic produce `5`. But for `20 / 3`, the Miden VM computes
`20 * 3^(-1) mod p`, which yields a large field element instead of integer `6`.

| Expression | `u64` result | `Felt` result |
|------------|--------------|---------------|
| `10 / 2` | `5` | `5` |
| `20 / 3` | `6` | `6148914689804861447` |

```rust
let exact_u64 = 10_u64 / 2; // 5
let exact_felt = felt!(10) / felt!(2); // 5

let integer_div = 20_u64 / 3; // 6
let field_div = felt!(20) / felt!(3); // 6148914689804861447
```

:::note For business logic, prefer u64
For computing amounts, balances, counters, or any value where overflow/underflow behavior matters, convert to `u64` first, perform the arithmetic, then convert back with `Felt::new()`.
:::
Expand Down
52 changes: 52 additions & 0 deletions docs/builder/tutorials/helpers/pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,57 @@ Failure to validate before subtraction can lead to:

---

## Felt Division Is Not Integer Division

### Problem

Using `/` on `Felt` values can look correct in simple cases and then break as soon as the division
is not exact.

```rust
let exact = Felt::new(10) / Felt::new(2); // 5
let unexpected = Felt::new(20) / Felt::new(3);
// 6148914689804861447, not 6
```

This is especially dangerous for token amounts, fee calculations, and proportional splits, where
developers usually expect integer division semantics.

### Why This Happens

`Felt` division in the Miden VM is field division. Instead of truncating or rounding, it multiplies
by the multiplicative inverse of the divisor in the Goldilocks field.

That means:

- `10 / 2` works out to `5`, so the behavior can look harmless at first
- `20 / 3` becomes `20 * 3^(-1) mod p`, which is `6148914689804861447`

This is correct field arithmetic, but it is not the same operation as Rust integer division.

### Solution

For business logic, convert to `u64`, do the integer arithmetic there, and convert back only after
the calculation is complete:

```rust
let total_amount: u64 = total.inner[0].as_u64();
let recipients: u64 = recipient_count.as_u64();

let share = total_amount / recipients;
let share_felt = Felt::from_u64_unchecked(share);
```

Use `Felt` division only when you explicitly need field semantics for protocol, algebraic, or
cryptographic logic.

:::warning Do not use Felt division for balances or pricing math
If the value represents an amount, balance, fee, or ratio that users reason about as an integer,
do the calculation in `u64`.
:::

---

## Wallet Component Requirement

### Problem
Expand Down Expand Up @@ -490,6 +541,7 @@ The native hash function changed from RPO to Poseidon2 in v0.14, so every MAST r
| Too many args | "4 words" error | Group into Words, use inputs |
| Array reversal | Wrong data order | Be consistent with construction |
| Felt underflow | Balance wraps to huge number | Validate before subtraction |
| Felt division | Huge unexpected result from `/` | Convert to `u64` first |
| Missing wallet | Asset operation fails | Add `BasicWallet` component |
| Key mismatch | Zero balances | Use helper function for keys |
| Note type | Wrong note visibility | Use 1 (Public) or 2 (Private) |
Expand Down