Skip to content
Pre-Release: Fe is under active development. This documentation covers the upcoming release. Follow progress on GitHub

Helper Structs

Helper structs are regular (non-storage) structs used to organize data and logic. They’re useful for grouping related values, returning multiple values, and creating reusable components.

Group related values into a struct:

struct TransferParams {
from: u256,
to: u256,
amount: u256,
}
fn execute_transfer(params: TransferParams) -> bool uses (mut store: TokenStorage) {
// Use params.from, params.to, params.amount
transfer(params.from, params.to, params.amount)
}

This is clearer than passing many individual parameters.

Use structs to return multiple values:

struct BalanceInfo {
balance: u256,
frozen: bool,
last_updated: u256,
}
fn get_account_info(account: u256) -> BalanceInfo uses (store: TokenStorage) {
BalanceInfo {
balance: store.balances.get(account),
frozen: store.frozen.get(account),
last_updated: store.timestamps.get(account),
}
}

Encapsulate configuration:

struct FeeConfig {
base_fee: u256,
percentage_fee: u256, // In basis points
max_fee: u256,
}
impl FeeConfig {
fn calculate(self, amount: u256) -> u256 {
let percentage = amount * self.percentage_fee / 10000
let total = self.base_fee + percentage
if total > self.max_fee {
self.max_fee
} else {
total
}
}
}
fn process_with_fee(amount: u256, config: FeeConfig) -> u256 {
let fee = config.calculate(amount)
amount - fee
}

Create structs for validated data:

struct ValidatedAmount {
pub value: u256,
}
impl ValidatedAmount {
pub fn new(value: u256, max: u256) -> ValidatedAmount {
if value > max {
revert(0, 0)
}
ValidatedAmount { value }
}
pub fn get(self) -> u256 {
self.value
}
}
fn safe_transfer(to: u256, amount: ValidatedAmount) -> bool uses (mut store: TokenStorage) {
// amount is guaranteed to be valid
transfer(caller(), to, amount.get())
}

Create structs for operation results:

struct TransferResult {
success: bool,
new_sender_balance: u256,
new_recipient_balance: u256,
}
fn transfer_with_result(
from: u256,
to: u256,
amount: u256
) -> TransferResult uses (mut store: TokenStorage) {
let from_bal = store.balances.get(from)
if from_bal < amount {
return TransferResult {
success: false,
new_sender_balance: from_bal,
new_recipient_balance: store.balances.get(to),
}
}
let new_from = from_bal - amount
let new_to = store.balances.get(to) + amount
store.balances.set(from, new_from)
store.balances.set(to, new_to)
TransferResult {
success: true,
new_sender_balance: new_from,
new_recipient_balance: new_to,
}
}

Structs with methods for calculations:

struct Percentage {
basis_points: u256,
}
impl Percentage {
fn new(bp: u256) -> Percentage {
Percentage { basis_points: bp }
}
fn from_percent(pct: u256) -> Percentage {
Percentage { basis_points: pct * 100 }
}
fn apply(self, value: u256) -> u256 {
value * self.basis_points / 10000
}
fn apply_inverse(self, value: u256) -> u256 {
value * 10000 / (10000 - self.basis_points)
}
}
// Usage
let fee_rate = Percentage::from_percent(3) // 3%
let fee = fee_rate.apply(1000) // 30

Use helper structs for complex construction:

struct TokenConfig {
name_hash: u256,
symbol_hash: u256,
decimals: u8,
initial_supply: u256,
mintable: bool,
burnable: bool,
}
impl TokenConfig {
fn new() -> TokenConfig {
TokenConfig {
name_hash: 0,
symbol_hash: 0,
decimals: 18,
initial_supply: 0,
mintable: false,
burnable: false,
}
}
fn with_decimals(mut self, d: u8) -> TokenConfig {
self.decimals = d
self
}
fn with_supply(mut self, supply: u256) -> TokenConfig {
self.initial_supply = supply
self
}
fn mintable(mut self) -> TokenConfig {
self.mintable = true
self
}
fn burnable(mut self) -> TokenConfig {
self.burnable = true
self
}
}
let config = TokenConfig::new()
.with_decimals(6)
.with_supply(1000000)
.mintable()

Group related operations using impl blocks:

struct MathUtils {
// Empty struct - just a namespace for functions
}
impl MathUtils {
fn min(a: u256, b: u256) -> u256 {
if a < b { a } else { b }
}
fn max(a: u256, b: u256) -> u256 {
if a > b { a } else { b }
}
fn clamp(value: u256, min_val: u256, max_val: u256) -> u256 {
MathUtils::max(min_val, MathUtils::min(value, max_val))
}
fn abs_diff(a: u256, b: u256) -> u256 {
if a > b { a - b } else { b - a }
}
}
// Usage
let clamped = MathUtils::clamp(value, 10, 100)

Helper structs can work alongside effects:

struct TransferRequest {
from: u256,
to: u256,
amount: u256,
}
impl TransferRequest {
fn new(to: u256, amount: u256) -> TransferRequest {
TransferRequest {
from: caller(),
to,
amount,
}
}
fn execute(self) -> bool uses (mut store: TokenStorage) {
transfer(self.from, self.to, self.amount)
}
fn validate(self) -> bool uses (store: TokenStorage) {
let balance = store.balances.get(self.from)
balance >= self.amount && self.to != 0
}
}
PatternUse Case
Data groupingCombine related parameters
Return typesReturn multiple values
ConfigurationEncapsulate settings
ValidationEnsure data correctness
ResultsRich operation outcomes
ComputationReusable calculations
BuilderComplex object construction
NamespaceGroup related functions