Rust Onchain: A Backend Engineer's Guide to Stylus
Every backend has business logic that people have to trust. Credit scoring, risk evaluation, eligibility checks. Your consumers call an API, get a result, and take your word for it. The computation is a black box. If a regulator asks how a score was calculated, you pull logs. If a partner disputes a result, you re-run the query and hope the data hasn't changed.
This works. It has worked for decades. But there's a growing category of applications where "trust us" isn't enough and where the computation itself needs to be independently verifiable without exposing the underlying rules.
That's a problem that Web3 generally solves for applications, and Arbitrum Stylus offers a practical way forward for backend engineers. Not by asking you to learn a new language or adopt a new paradigm, but by giving you a new deployment target for the Rust you already write.
What Stylus actually is
If you write Rust, you can write a Stylus contract. You compile your code to WebAssembly, deploy it to Arbitrum (a blockchain network optimized for low-cost computation), and it runs onchain. That means every execution is publicly verifiable, deterministic, and tamper-proof without you having to build any of that infrastructure yourself.
Most blockchain development requires learning Solidity, a domain-specific language built for smart contracts. Stylus skips that entirely. Your Rust code compiles to WASM and runs in a dedicated runtime alongside the existing blockchain infrastructure. You keep your language, your toolchain, and your crates. The blockchain handles the trust layer. And if you do end up working with existing smart contracts written in Solidity, your Stylus code operates right alongside them, sharing the same state and able to call each other directly.
So why would you run something onchain instead of locally? Because you gain properties that no backend can offer on its own: every execution is independently auditable, the logic can't be silently changed after deployment, and your partners don't have to take your word for how a score was calculated.
The tradeoff used to be that onchain compute was prohibitively expensive for anything nontrivial. Stylus changes that equation, and we'll get into the numbers later. For now, the practical implication is that you can move compute-heavy logic onchain and call it from your existing services the same way you'd call any other microservice.
The architecture: blockchain as a compute layer
To make this concrete, here's a scoring engine. The architecture is standard microservices, with one twist: the scoring computation happens onchain via a Stylus contract instead of in the API server.
The codebase has three pieces: a Stylus contract that does the scoring, an Axum API server that owns the business rules and talks to the contract, and a shared Rust crate that defines the types both sides use.
The API server is a normal Rust backend. It stores scoring rules in SQLite, accepts requests over REST, and returns results. The only difference from a typical service is that the POST /score endpoint doesn't compute the score locally. It sends the rules and entity data to a Stylus contract on Arbitrum and returns the contract's result.
Same types, both sides of the chain
The shared crate is where things get interesting. It defines the types used by both the onchain contract and the offchain API server:
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use alloc::collections::BTreeMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Entity {
pub factors: BTreeMap<String, i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rule {
pub factor_name: String,
pub weight: i64,
pub threshold: i64,
pub direction: Direction,
pub penalty_or_bonus: i64,
}
The crate compiles as no_std + alloc when the contract imports it, and with full std when the API server imports it. Same types, same serde derives, same validation logic. One crate, two compilation targets.
This is impossible with Solidity. If your backend is in TypeScript or Go and your contract is in Solidity, you maintain two separate type definitions and hope they stay in sync. With Stylus, the Rust compiler guarantees they match.
The scoring contract: a pure function
The Stylus contract itself is a stateless compute engine. It stores nothing onchain. It receives factor values and rule parameters as arrays, runs iterative scoring with cross-factor correlation, and returns the result.
#[public]
impl ScoringEngine {
pub fn evaluate(
&self,
factors: Vec<i64>,
weights: Vec<i64>,
thresholds: Vec<i64>,
directions: Vec<i64>,
bonuses: Vec<i64>,
corr_a: Vec<u32>,
corr_b: Vec<u32>,
corr_weights: Vec<i64>,
iterations: u32,
) -> Vec<i64> {
// iterative convergence scoring
for iteration in 0..iters {
// each pass refines the score using the previous pass as a bias
for i in 0..num_rules {
let weight = I40F24::from_num(weights[i]);
// threshold gate + weighted fixed-point math
}
}
// cross-factor correlation with damped oscillation
for i in 0..num_corr {
let interaction = norm_a * norm_b * pair_weight;
for k in 1..=50 {
let decay = I40F24::from_num(1) / I40F24::from_num(k);
damped = damped * decay + interaction * decay;
}
}
vec![score, tier, correlation_adjustment, iterations as i64]
}
}
A few things to notice. The fixed crate's I40F24 type gives you precise weighted arithmetic without floating-point, which matters when scores need to be deterministic across every execution. The algorithm runs multiple passes over the rules (each pass refining the score using the previous result as input) and then evaluates every pair of correlated factors through a series of decay calculations. That's a lot of looping and a lot of math per call.
This is standard Rust. You'd write this the same way for a local service. The only thing that makes it a smart contract is the #[public] attribute and the #[storage] / #[entrypoint] macros. Everything else is just your code.
What the numbers look like
Stylus contracts compile to WASM and execute at near-native speed. For compute-heavy operations, the cost savings are significant. On a blockchain, computation costs "gas," which is the unit you pay for every operation your code executes onchain. Think of it like CPU billing on a cloud provider, but per instruction. The more your code loops and calculates, the more gas it costs.
To quantify this, the scoring engine repo includes an equivalent Solidity contract implementing the same algorithm, and a /benchmark endpoint that runs all three side by side: offchain Rust, onchain Stylus, and onchain Solidity. Solidity is a standard language for writing smart contracts. By implementing the same scoring algorithm in both Stylus and Solidity, we can compare the cost of running identical logic in each environment.
In our benchmark (10 scoring factors, 10 cross-factor correlations, 50 refinement passes), Stylus used over 90% less gas than the equivalent Solidity contract for the same workload. The exact numbers will vary depending on your environment, but the relative savings are consistent: the heavier the computation, the wider the gap.
This lines up with what production teams are seeing. RedStone, an oracle framework that feeds real-world data (prices, rates, metrics) into blockchain applications, ported their verification logic from Solidity to Stylus and published the results:
The pattern is clear: storage operations cost the same on both VMs, but iterative math, weighted scoring, and any logic that loops over data is where WASM pulls ahead. You can clone the repo, run pnpm benchmark, and see the comparison for yourself with your own workload.
Why this matters for production systems
The value proposition isn't "blockchain makes everything better." It's narrower and more useful than that.
Auditability without exposure. The scoring rules stay in your database. The algorithm executes onchain where anyone can verify that a given input produces a given output. You prove correctness without revealing your ruleset.
Tamper-proof computation. Once the contract is deployed, the scoring logic cannot be silently modified. Your partners and regulators can verify that the same code is running today that was audited last quarter.
Deterministic execution. Same inputs, same outputs, every time. No race conditions, no stale caches, no "works on my machine." The blockchain is the most opinionated compute environment you'll ever use, and for scoring and validation workloads, that's a feature.
Incremental adoption. You don't rip out your backend. Your API stays your API. Your database stays your database. You add one component, the onchain scoring call, and gain verifiability for the logic that matters most.
Try it yourself
The full project is on GitHub: stylus-scoring-engine. Clone it and run the whole thing in four commands:
git clone https://github.com/hummusonrails/stylus-scoring-engine.git
cd stylus-scoring-engine && pnpm install
pnpm devnode # start local arbitrum node
pnpm deploy:all # build and deploy both contracts
pnpm api # start the api server
pnpm benchmark # run the three-way gas comparison
The benchmark adds 10 scoring rules, sends a request with 10 factors and 10 cross-factor correlation pairs through 50 convergence iterations, and prints the gas cost for offchain Rust, onchain Stylus, and onchain Solidity side by side. Swap in your own scoring logic and see where the compute savings land for your workload.
The bigger picture
You don't need to become a blockchain developer to use this. The question is simpler than that: is there logic in your system where verifiable execution would be valuable?
If you're building scoring systems, validation pipelines, compliance checks, or any computation where "trust me" is starting to feel insufficient, Stylus gives you a path. Write the same Rust you already write. Share types across the chain boundary. Deploy one function onchain and keep everything else exactly where it is.
The barrier between your backend and the blockchain just got a lot thinner.