Protocol contract · Non-negotiable

Wallet Invariants

Global invariants for every Unit wallet movement. Send, Deposit, and Withdraw differ by funding source — and the rules differ with them. Display values must never become executable values.

Source of truth
§ 0 · The Global Principle

Percentage controls signal known internal balance

The single rule from which most others follow.

Only flows that spend from a known internal Unit balance may use percentage-based inputs.
✓
SEND spends an internal Unit balance — quick-fill required.
✓
WITHDRAW spends an internal Unit-wrapped balance — quick-fill required.
✕
DEPOSIT is an external inflow — quick-fill forbidden.
Why this is the rule: the existence of percentage controls is a SIGNAL to the user that the system knows their balance. Showing them where the system does not know the balance teaches false behavior, regardless of any "convenience" intent.
§ 1 · Module comparison

Send vs Deposit vs Withdraw at a glance

Same lifecycle shape. Different funding models. Different rules.

Aspect Send Deposit Withdraw Receive Activity Vault
Funding source Internal Unit balance External chain (unknown) Internal wrapped Unit balance External (incoming) — Internal vault stake
Asset type Internal Unit asset External native (BTC, ETH, …) Wrapped Unit-native (BTCU, …) External native — Internal Unit asset
Quick-fill (25/50/75/Max) Required Forbidden Required N/A N/A Allowed
"Available balance" surface Required Forbidden Required — — Allowed
Address book / saved address Optional (recipients) Single-use deposit address Required (matching chain) N/A N/A —
Optional tx-hash submission No User-facing Operator-only — — —
Forbidden post-submit causes USER_CANCELLED, USER_REJECTED, INSUFFICIENT_BALANCE USER_CANCELLED, USER_REJECTED, BELOW_MINIMUM USER_CANCELLED, USER_REJECTED, INSUFFICIENT_BALANCE — — USER_CANCELLED, USER_REJECTED
§ 2 · Display ≠ executable

Decimal precision discipline

Four kinds of precision. Never confused. Display rounding is for reading; the executable amount preserves full asset precision.

A

Computational precision

  • Full chain-defined decimals
  • Used for arithmetic + storage
  • Never lossy
B

Validation precision

  • Asset's allowed decimal count
  • Rejects over-precision input
  • Never silently rounds to "valid"
C

Display precision

  • Readability rounding only
  • Output of toFixed / fiat formatter
  • NEVER reused as executable
D

Input precision

  • What the input field accepts
  • Mode-aware (token vs USD)
  • Always normalized to token before validation
Rule: the executable amount sent to chain is computed from the user's typed token quantity, parsed via parseUnits(_, asset.decimals). It is independent of every toFixed() in the codebase.
§ 3 · Asset decimal standard

Per-asset native precision

Asset Native decimals Display
BTCU / BTC8up to 8
ETHU / ETH18 (computational)6–8 significant
BNBU / BNB18 (computational)6–8 significant
SOLU / SOL9up to 9
TONU / TON9up to 9
USDTU / USDT6up to 6
USDCU / USDC6up to 6
$UNITCOINfrom chain configfrom chain config
Fiat (USDU, EURU, …)chain config2 decimals
Other tokens: precision MUST be supplied by token metadata. If precision is unknown, executable submission is blocked — never guessed.
§ 4 · Error safety

No raw backend enums in user UI

Internal rejection reasons stay internal. Users see one safe sentence that tells them what they need to know.

Internal (admin / ops)

Never user-facing

  • UNDERPAID, OVERPAID
  • AMOUNT_MISMATCH
  • WRONG_ASSET, WRONG_NETWORK
  • TX_NOT_FOUND, TX_REUSED
  • DepositNotPending, TxHashAlreadySubmitted
User-facing (safe set)

From userFacingReason

  • "Transaction not found on chain"
  • "Transaction did not match your deposit"
  • "This transaction has already been used"
  • "This deposit is already confirmed."
  • Fallback: "This deposit could not be verified."
Rule: the resolver (rejectionReason, userFacingReason) → string returns ONLY allowed strings. Tests assert that no raw enum identifier appears in user copy.
§ 5 · Universal user contract

What the user must always know

Every Unit wallet UI must continuously expose six facts.

✓
Source of funds — where the value originates.
✓
Destination of funds — where the value lands.
✓
Asset being moved — exact uppercase ticker.
✓
Value being moved — executable, not display-rounded.
✓
Network involved where applicable — exact chain.
✓
Internal vs external — and which side is which.