Checked 256-bit integer arithmetic host functions
Specification
Preamble
CAP: 0082
Title: Checked 256-bit integer arithmetic host functions
Working Group:
Owner: Jay Geng <@jayz22>
Authors: Leigh McCulloch <@leighmcculloch>, Jay Geng <@jayz22>
Consulted: Özgün Özerk <@ozgunozerk>
Status: Final
Created: 2026-02-24
Discussion: https://github.com/orgs/stellar/discussions/1834
Protocol version: 26
Simple Summary
This CAP adds checked variants of the existing 256-bit integer arithmetic host functions. Unlike the existing functions which trap on overflow, the checked variants return Void on overflow, allowing contracts to handle arithmetic errors gracefully.
Working Group
As described in the preamble section.
Motivation
The existing 256-bit integer arithmetic host functions (u256_add, i256_sub, etc.), defined in CAP-0046-03, trap on overflow by emitting an ScError that halts contract execution. This behavior prevents contracts from recovering from arithmetic overflow — a scenario that is expected and common in financial math.
Developers building math libraries on Soroban — such as fixed-point arithmetic, WAD-style decimal math, and other numerical utilities — need the ability to detect overflow and handle it gracefully. For example, a fixed-point division function may need to check for intermediate overflow and fall back to an alternative computation path, or return a user-facing error rather than aborting the entire transaction.
Rust's standard library provides this pattern through checked_* methods (e.g., checked_add, checked_mul) that return Option<T> — either Some(result) on success or None on overflow. The existing Soroban host functions have no equivalent; they are analogous to Rust's panicking arithmetic operators.
Implementing overflow detection on the guest side (in Wasm) for 256-bit integers is expensive, requiring multiple 64-bit comparisons and carry propagation, consuming far more CPU instructions than a single host function call that performs the same check natively.
Goals Alignment
This CAP is aligned with the following Stellar Network Goals:
- The Stellar Network should make it easy for developers of Stellar projects to create highly usable products.
- The Stellar Network should run at scale and at low cost to all participants of the network.
Abstract
Eight new host functions are proposed: four for unsigned 256-bit integers (U256) and four for signed 256-bit integers (I256), covering addition, subtraction, multiplication, and exponentiation. Each checked function mirrors an existing trapping arithmetic function but returns Val — either the computed result (as U256Val or I256Val) or Void to signal overflow. This enables contracts to handle arithmetic overflow without halting execution, following the checked_* convention from Rust's standard library.
Specification
New host functions
The following eight host functions are added to the int module (export "i"), continuing after the last existing export "G" (duration_obj_to_u64).
{
"export": "H",
"name": "u256_checked_add",
"args": [
{ "name": "lhs", "type": "U256Val" },
{ "name": "rhs", "type": "U256Val" }
],
"return": "Val",
"docs": "Performs checked addition. Computes `lhs + rhs`, returning `Void` if overflow occurred, otherwise returns `U256Val`.",
"min_supported_protocol": 26
},
{
"export": "I",
"name": "u256_checked_sub",
"args": [
{ "name": "lhs", "type": "U256Val" },
{ "name": "rhs", "type": "U256Val" }
],
"return": "Val",
"docs": "Performs checked subtraction. Computes `lhs - rhs`, returning `Void` if overflow occurred, otherwise returns `U256Val`.",
"min_supported_protocol": 26
},
{
"export": "J",
"name": "u256_checked_mul",
"args": [
{ "name": "lhs", "type": "U256Val" },
{ "name": "rhs", "type": "U256Val" }
],
"return": "Val",
"docs": "Performs checked multiplication. Computes `lhs * rhs`, returning `Void` if overflow occurred, otherwise returns `U256Val`.",
"min_supported_protocol": 26
},
{
"export": "K",
"name": "u256_checked_pow",
"args": [
{ "name": "lhs", "type": "U256Val" },
{ "name": "rhs", "type": "U32Val" }
],
"return": "Val",
"docs": "Performs checked exponentiation. Computes `lhs.exp(rhs)`, returning `Void` if overflow occurred, otherwise returns `U256Val`.",
"min_supported_protocol": 26
},
{
"export": "L",
"name": "i256_checked_add",
"args": [
{ "name": "lhs", "type": "I256Val" },
{ "name": "rhs", "type": "I256Val" }
],
"return": "Val",
"docs": "Performs checked addition. Computes `lhs + rhs`, returning `Void` if overflow occurred, otherwise returns `I256Val`.",
"min_supported_protocol": 26
},
{
"export": "M",
"name": "i256_checked_sub",
"args": [
{ "name": "lhs", "type": "I256Val" },
{ "name": "rhs", "type": "I256Val" }
],
"return": "Val",
"docs": "Performs checked subtraction. Computes `lhs - rhs`, returning `Void` if overflow occurred, otherwise returns `I256Val`.",
"min_supported_protocol": 26
},
{
"export": "N",
"name": "i256_checked_mul",
"args": [
{ "name": "lhs", "type": "I256Val" },
{ "name": "rhs", "type": "I256Val" }
],
"return": "Val",
"docs": "Performs checked multiplication. Computes `lhs * rhs`, returning `Void` if overflow occurred, otherwise returns `I256Val`.",
"min_supported_protocol": 26
},
{
"export": "O",
"name": "i256_checked_pow",
"args": [
{ "name": "lhs", "type": "I256Val" },
{ "name": "rhs", "type": "U32Val" }
],
"return": "Val",
"docs": "Performs checked exponentiation. Computes `lhs.exp(rhs)`, returning `Void` if overflow occurred, otherwise returns `I256Val`.",
"min_supported_protocol": 26
}
XDR changes
This CAP does not introduce any XDR changes. The new host functions perform the same arithmetic as the existing 256-bit integer functions and reuse the existing ContractCostType entries for metering.
Semantics
Each checked function mirrors the semantics of the corresponding existing function (defined in CAP-0046-03), with one key difference: instead of trapping on overflow, the checked variant returns Void.
The return type Val can be either:
- The computed result (as
U256ValorI256Val) on success. Voidon overflow.
New host functions introduced
u256_checked_add
Description: performs checked addition. Computes lhs + rhs.
Cost: Int256AddSub.
Return value: returns the result as U256Val on success. Returns Void if the addition overflows U256::MAX.
u256_checked_sub
Description: performs checked subtraction. Computes lhs - rhs.
Cost: Int256AddSub.
Return value: returns the result as U256Val on success. Returns Void if rhs > lhs (unsigned underflow).
u256_checked_mul
Description: performs checked multiplication. Computes lhs * rhs.
Cost: Int256Mul.
Return value: returns the result as U256Val on success. Returns Void if the multiplication overflows U256::MAX.
u256_checked_pow
Description: performs checked exponentiation. Computes lhs.exp(rhs).
Cost: Int256Pow.
Return value: returns the result as U256Val on success. Returns Void if the exponentiation overflows U256::MAX.
i256_checked_add
Description: performs checked addition. Computes lhs + rhs.
Cost: Int256AddSub.
Return value: returns the result as I256Val on success. Returns Void if the addition overflows (i.e., result exceeds I256::MAX or falls below I256::MIN).
i256_checked_sub
Description: performs checked subtraction. Computes lhs - rhs.
Cost: Int256AddSub.
Return value: returns the result as I256Val on success. Returns Void if the subtraction overflows.
i256_checked_mul
Description: performs checked multiplication. Computes lhs * rhs.
Cost: Int256Mul.
Return value: returns the result as I256Val on success. Returns Void if the multiplication overflows.
i256_checked_pow
Description: performs checked exponentiation. Computes lhs.exp(rhs).
Cost: Int256Pow.
Return value: returns the result as I256Val on success. Returns Void if the exponentiation overflows.
Design Rationale
Not included: checked_{div, rem_euclid, shl, shr}
Checked variants for division, Euclidean remainder, shift left, and shift right are not included because their error conditions are trivially detectable on the guest side before calling the existing trapping function:
- Division and remainder: the only pre-checkable error condition is
rhs == 0(and fori256_div, alsolhs == I256::MIN && rhs == -1). A simple comparison on the guest is cheap and avoids the need for a new host function. - Shifts: the error condition is
rhs >= 256, which is a single comparison on the guest.
Return type Val with Void for overflow
The return type is Val rather than U256Val/I256Val because the function must be able to return two kinds of values: the numeric result on success, or Void on overflow. Void is the natural representation of "no value" in Soroban's value type system. On the SDK side, this can additionally be mapped to an Option type, where Void corresponds to None.
No new cost types
The checked functions perform exactly the same arithmetic as their trapping counterparts. The only difference is the error path: returning Void instead of trapping. Constructing a Void value is negligible in cost (it is a constant, zero-body tagged value), so the existing ContractCostType entries (Int256AddSub, Int256Mul, Int256Pow) are sufficient for metering.
Why not change existing functions
Changing the existing functions to return Void instead of trapping would break backwards compatibility. Existing contracts rely on the trapping behavior for implicit error propagation — when an overflow occurs, the transaction aborts automatically without requiring explicit error handling. The trapping behavior remains the right default for contracts that do not need to handle overflow.
Protocol Upgrade Transition
The proposed host functions will become available in protocol 26.
Backwards Incompatibilities
This CAP does not introduce any backward incompatibilities. The existing 256-bit integer arithmetic host functions remain unchanged. The new checked variants are purely additive.
Resource Utilization
No new cost types are introduced. The new host functions reuse existing metering for 256-bit integer operations. The Void return path on overflow is negligible in cost.
Security Concerns
- Denial of service: Proper metering is ensured by reusing the existing cost types. No new attack surface is introduced.
- Correctness of overflow detection: The implementation must produce exactly the same overflow conditions as the existing trapping functions. Any discrepancy could lead to contracts incorrectly assuming an operation succeeded when it should have returned
Void, or vice versa.
Test Cases
TODO
Implementation
TODO
Preamble
Discussion
0 linked threads