Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Return Values

The return action produces a value from a route. view and pure routes must include one; it provides the mechanism for returning computation results to callers.

Syntax

return(expression)
return(expr1, expr2)   // multi-value, for `-> (T1, T2)`

The parentheses are required. The expression inside is evaluated and returned to the caller.

In View Routes

A view route should end with a return action:

view get_balance() -> U256 => [
    return(m_balance)
]

view get_owner() -> address => [
    return(m_owner)
]

In Pure Routes

Likewise for pure routes:

pure add(a: u64, b: u64) -> u64 => [
    return(a + b)
]

pure compute_fee(amount: U256, bps: u64) -> U256 => [
    return(amount * (bps as U256) / 10000)
]

Computed Returns

The return expression can be any ordinary expression (arithmetic, function calls, member reads). if, match, and { ... } blocks are not allowed directly inside return(...); bind them with let first:

view get_status_label() -> String => [
    let label = match m_status {
        Status::Active => "active",
        Status::Paused => "paused",
        _ => "unknown"
    };
    return(label)
]
view get_effective_balance(account: address) -> U256 => [
    let effective = {
        let raw = m_balances[account];
        let locked = m_locked[account];
        raw - locked
    };
    return(effective)
]

Return with Let Bindings

Let bindings can compute intermediate values before the return:

view get_share(account: address) -> U256 => [
    let balance = m_balances[account];
    let total = m_total_supply;
    let share = if total > 0 {
        balance * 10000 / total
    } else {
        0
    };
    return(share)
]

Return Type Agreement

The type of the return expression must match the declared return type of the route. A route that declares -> T but never returns a value is rejected, and so is a value of an obviously different kind (a string for a number, say):

// Correct: returns U256 as declared
view total() -> U256 => [
    return(m_supply)
]

// Rejected: return type mismatch
// view total() -> U256 => [
//     return("not a number")
// ]

Integer widths are not yet diagnosed here, so make them agree yourself: to return a U256 from a -> u8 route, narrow it with as (which reverts on overflow) or declare the wider return type.

Multiple return values

Declare a tuple return type and pass several expressions to return:

view stats() -> (U256, bool) => [
    return(m_total, m_total > 0)
]

Return in Regular Routes

Regular routes typically do not use return – their purpose is to cause side effects (state changes, message sends) rather than produce values. If a regular route does not need to return a value, the action list contains only sends, deploys, conditionals, and let bindings. A regular route that declares -> T may still return(...); the returned expression sees member values after the route’s transforms.

Complete Example

An entity exposing several computed views:

entity Pool {
    routes {
        view total_liquidity() -> U256 => [
            return(m_reserve_a + m_reserve_b)
        ]

        view price(token: address) -> U256 => [
            let p = if token == m_token_a {
                m_reserve_b * 1_000_000 / m_reserve_a
            } else {
                m_reserve_a * 1_000_000 / m_reserve_b
            };
            return(p)
        ]

        view share_of(provider: address) -> U256 => [
            let lp = m_lp_balances[provider];
            let total_lp = m_total_lp;
            let share = if total_lp > 0 {
                lp * (m_reserve_a + m_reserve_b) / total_lp
            } else {
                0
            };
            return(share)
        ]

        pure estimate_output(amount_in: U256, reserve_in: U256, reserve_out: U256) -> U256 => [
            return(amount_in * reserve_out / (reserve_in + amount_in))
        ]
    }

    // set by the pool's mutating routes (omitted)
    m_token_a: address {}
    m_reserve_a: U256 {}
    m_reserve_b: U256 {}
    m_lp_balances: HashMap<address, U256> {}
    m_total_lp: U256 {}
}