Structs, Enums & Custom Types
SolidCash provides composite data types for organizing complex data: structs, enums, user-defined value types, and fixed-point decimals.
Structs
Structs group related fields into a single composite type.
struct User {
bytes20 addr;
string name;
int balance;
}
Defining Structs
Structs can be defined at file level (importable across files) or inside a contract:
// File-level struct (importable)
struct Pair { int x; int y; }
contract MyContract() {
// Contract-level struct
struct Config { bytes20 admin; int threshold; }
}
Creating Struct Instances
User user = User(hex"abc123...", "Alice", 100);
Pair p = Pair(10, 20);
Field Access
int balance = user.balance;
string name = user.name;
Structs in State
Structs are stored as concatenated fixed-width fields in NFT commitments:
contract Registry() {
mapping(bytes20 => User) public users;
function register(bytes20 key, string name, int balance) {
this.users[key] = User(key, name, balance);
}
// Partial field update — only modifies `name`, keeps other fields
function rename(bytes20 key, string newName) {
this.users[key].name = newName;
}
}
Nested Structs
struct Address { bytes20 hash; bool isToken; }
struct Account { Address addr; int balance; }
Importing Structs
import { User, Pair } from "./types.scash";
Enums
Enums define a set of named constants.
enum Status { PENDING, ACTIVE, COMPLETED } // values: 0, 1, 2
Custom Values
Unlike Solidity, SolidCash allows custom enum values:
enum Priority { LOW = 1, MEDIUM = 5, HIGH = 10 }
Using Enums
Status current = Status.ACTIVE;
require(current == Status.ACTIVE);
Type Introspection
int minStatus = type(Status).min; // 0
int maxStatus = type(Status).max; // 2
int minPrio = type(Priority).min; // 1
int maxPrio = type(Priority).max; // 10
Scope
Enums can be file-level or contract-level. File-level enums are importable:
import { Status } from "./enums.scash";
User-Defined Value Types (UDVT)
Type aliases create zero-cost semantic wrappers around existing types:
type TokenId is bytes32;
type Amount is int;
UDVTs prevent accidental mixing of semantically different values — a TokenId cannot be assigned where an Amount is expected without explicit conversion.
Wrapping & Unwrapping
TokenId id = TokenId.wrap(someBytes32);
bytes32 raw = TokenId.unwrap(id);
Using-For Directives
The using directive attaches library functions to types, enabling method-call syntax:
library SafeMath {
function add(int a, int b) returns (int) {
int result = a + b;
require(result >= a);
return result;
}
}
using SafeMath for int;
// Now callable as a method:
int result = x.add(y); // equivalent to SafeMath.add(x, y)
Operator Overloading
Map specific functions to operators:
using { add as +, sub as - } for int;
Global Scope
using SafeMath for int global; // available in all contracts in this file
Fixed-Point Decimal Types
SolidCash is the first smart contract language with a full fixed-point implementation. Solidity declared the type but never built it.
fixed128x18 price = 1.5; // stored as 1_500_000_000_000_000_000
ufixed128x18 rate = 0.05; // stored as 50_000_000_000_000_000
Format
fixed<M>x<N> where:
- M = total bits (8 to 256, in steps of 8)
- N = decimal places (0 to 80)
The shorthand fixed is equivalent to fixed128x18. Similarly ufixed is ufixed128x18.
Arithmetic
All arithmetic operators work with automatic scale correction:
fixed128x18 a = 1.5;
fixed128x18 b = 2.0;
fixed128x18 sum = a + b; // 3.5
fixed128x18 diff = b - a; // 0.5
fixed128x18 product = a * b; // 3.0 (auto-scales intermediate result)
fixed128x18 quotient = b / a; // 1.333...
There is no intermediate overflow risk. Bitcoin Cash supports arbitrary-precision integers (since May 2025 BigInt activation), so intermediate products up to ~80,000 bits are handled safely.
Type Introspection
fixed128x18 minVal = type(fixed128x18).min;
fixed128x18 maxVal = type(fixed128x18).max;
Bounded Integer Types
Signed and unsigned integers with explicit bit widths:
int8 small = 127; // range: -128 to 127
uint8 byte_val = 255; // range: 0 to 255
int256 large = 0; // 256-bit signed
uint256 huge = 0; // 256-bit unsigned
Available in 8-bit increments: int8, int16, ..., int256 and uint8, uint16, ..., uint256.
Introspection
int min = type(int8).min; // -128
int max = type(int8).max; // 127
int umax = type(uint8).max; // 255
Unchecked Arithmetic
By default, bounded integer arithmetic checks for overflow/underflow. Use unchecked blocks for wrapping behavior:
unchecked {
uint8 a = uint8(255) + uint8(1); // wraps to 0
uint8 b = uint8(0) - uint8(1); // wraps to 255
}
Type Introspection Summary
| Expression | Returns | Description |
|---|---|---|
type(int256).min | int | Minimum value |
type(uint8).max | int | Maximum value |
type(fixed128x18).min | fixed128x18 | Min fixed-point value |
type(Status).min | int | First enum value |
type(Status).max | int | Last enum value |
type(IToken).interfaceId | bytes32 | EIP-165-style interface ID |
type(MyContract).name | string | Contract name as string |