Skip to main content

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...
note

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

ExpressionReturnsDescription
type(int256).minintMinimum value
type(uint8).maxintMaximum value
type(fixed128x18).minfixed128x18Min fixed-point value
type(Status).minintFirst enum value
type(Status).maxintLast enum value
type(IToken).interfaceIdbytes32EIP-165-style interface ID
type(MyContract).namestringContract name as string