Data Structures¶
StxScript provides maps, lists, and tuples for organizing data in contracts.
Maps¶
Maps are key-value stores declared at the contract level. They are the primary mechanism for persistent storage in Clarity contracts.
Declaration¶
map balances<principal, uint>;
map allowances<{ owner: principal, spender: principal }, uint>;
map metadata<uint, { name: string, uri: string }>;
Generated Clarity:
(define-map balances principal uint)
(define-map allowances { owner: principal, spender: principal } uint)
(define-map metadata uint { name: (string-utf8 ...), uri: (string-utf8 ...) })
Reading from Maps¶
get returns an Optional value since the key may not exist:
Generated Clarity:
Use match or unwrap operators to handle the Optional:
@readonly
function get_balance(account: principal): uint {
match balances.get(account) {
some(b) => b,
none => 0u
}
}
Writing to Maps¶
set inserts or updates a value:
Generated Clarity:
Deleting from Maps¶
delete removes a key-value pair:
Generated Clarity:
Composite Keys¶
Maps can have tuple keys for multi-dimensional lookups:
map allowances<{ owner: principal, spender: principal }, uint>;
@public
function approve(spender: principal, amount: uint): Response<bool, uint> {
let key = { owner: tx-sender, spender: spender };
allowances.set(key, amount);
return ok(true);
}
@readonly
function get_allowance(owner: principal, spender: principal): uint {
let key = { owner: owner, spender: spender };
match allowances.get(key) {
some(amount) => amount,
none => 0u
}
}
Lists¶
Lists are fixed-length sequences of values of the same type.
List Literals¶
let numbers: List<uint> = [1u, 2u, 3u, 4u, 5u];
let names: List<string> = ["Alice", "Bob", "Charlie"];
let flags: List<bool> = [true, false, true];
Generated Clarity:
Higher-Order Functions¶
StxScript provides map, filter, and fold for list processing:
map¶
Apply a function to each element, producing a new list:
Generated Clarity:
filter¶
Keep elements matching a predicate:
fold¶
Reduce a list to a single value with an accumulator:
let sum = fold(numbers, 0u, (acc, x) => acc + x);
let product = fold(numbers, 1u, (acc, x) => acc * x);
Generated Clarity:
Combining Operations¶
@public
function process(numbers: List<uint>): Response<uint, uint> {
let evens = filter(numbers, (x) => x % 2u == 0u);
let doubled = map(evens, (x) => x * 2u);
let total = fold(doubled, 0u, (acc, x) => acc + x);
return ok(total);
}
Tuples¶
Tuples are named-field structures, similar to objects in TypeScript or structs in Rust.
Creating Tuples¶
Generated Clarity:
Typed Tuples¶
Accessing Fields¶
Use dot notation to read fields:
Generated Clarity:
Tuples as Function Parameters¶
@public
function update_user(data: { name: string, balance: uint }): Response<bool, uint> {
let name = data.name;
let balance = data.balance;
return ok(true);
}
Tuples as Map Keys¶
Tuples are commonly used as composite map keys:
Patterns¶
Token Balance Pattern¶
map balances<principal, uint>;
function credit(account: principal, amount: uint): bool {
let current = match balances.get(account) {
some(b) => b,
none => 0u
};
balances.set(account, current + amount);
return true;
}
function debit(account: principal, amount: uint): Response<bool, uint> {
let current = match balances.get(account) {
some(b) => b,
none => 0u
};
if (current < amount) {
return err(ERR_INSUFFICIENT_BALANCE);
}
balances.set(account, current - amount);
return ok(true);
}
Registry Pattern¶
map registry<uint, { owner: principal, data: string }>;
let next_id: uint = 0u;
@public
function register(data: string): Response<uint, uint> {
let id = next_id + 1u;
next_id = id;
registry.set(id, { owner: tx-sender, data: data });
return ok(id);
}
Next Steps¶
- Error Handling - Handling Optional from map lookups
- Control Flow - Iterating with for/while
- Functions - Using data structures in functions