9lives open sourced, Lit Protocol, How to forknet test Stylus
Stylus Saturdays, Part 4, Jan 5th, 2025
Merry Christmas and Happy New Year everyone! It’s been a little while since we’ve posted. Frankly, I’ve been super busy launching Superposition to mainnet, as well as developing 9lives. I’m pleased to share that we’ve open sourced 9lives!
9lives is Arbitrum’s most capital efficient prediction market, deployed currently on testnet (pending SPN on mainnet), and the first with the following features:
Fully permission-less and instant creation of markets. Creators receive a cut of all trading taking place on markets they create. The first community owned prediction market! We hope to incentivise X personalities to create markets with their communities, replicating the pump.fun experience to a degree.
Opinion polling resolver. Vote against each other for opinions you feel strongly about!
Contract resolver for settling markets, based on on-chain state. Useful with a price oracle for instant closure of markets as they conclude/hit a target amount.
AI resolver for settlement. SarpAI is a AI backend currently being developed by the team that can optionally resolve outcomes on your behalf, based on news developments.
A new commitment and reveal oracle powered by ARB and Staked ARB. We reaffirm our proud and ongoing commitment to the Arbitrum ecosystem by supporting staking your (already staked for voting) ARB to receive extra yield. At first, this will be exclusively ARB, but we look forward to the release of Staked ARB to support this!
The first implementation of the Dynamic Pari-Mutuel Market model for resolving markets. The Dynamic Pari-Mutuel (DPM) is a model invented in the early 2000s for liquidity free prediction market trading. Read more about it at via the app!
The first to support an optional concentrated liquidity AMM backend. An optional and custom AMM backend is slated to be enabled after some more thorough testing. A shortcoming of the original design of the DPM is being stuck with two outcomes. The AMM model enables potentially dozens of outcomes to be traded in the same capital space.
Trading of token representations powered by Longtail (Longtail Pro announcement coming soon - spot/perps exchange functionality backstopped by liquidity on-chain). We ran a successful experiment on Arbitrum One using Camelot that went great! Thank you to everyone who traded the presidential election.
Pump.fun-like experience for the creation of markets, instant up to date updates of trading taking place live.
We’re super proud of how this turned out so far, and we feel it really shows off how much we’ve learned since we begun our Stylus journey. However, just like any early stage release project, we’re refining elements of the experience to make it as seamless as possible. I’d like to use this opportunity again to thank the Offchain Labs team for making this technology, and for empowering developers such as us. Developing with Stylus is such a pleasure.
We’ve pioneered a few interesting techniques we think are of interest to Stylus developers:
Use of a strongly typed language as the basis for internal discussion of the infrastructure market/prediction market interplay: https://github.com/fluidity-money/9lives.so/blob/main/concept.ml. We then used AI to convert this GADT type definition to a Mermaid Chart for simple explanation via Github.
Use of two advanced testing techniques interwoven together: mutation and property testing. Property testing is the random variation of inputs, using binary search to identify issues when the inputs break code. Mutation testing is the random variation of code control flow, then the testing of how many “mutants” are caught by the testing. Mutation testing is useful for identifying shortcomings in testing code. All type-assisted programming is restating the problem domain: and mutation testing is an affordable tool to identify problems with testing your assumptions about the code. We used this mostly for the infrastructure market: https://github.com/fluidity-money/9lives.so/blob/main/src/storage_infra_market.rs#L157 - you can really see how this could be used in lieu of fixtures with https://github.com/fluidity-money/9lives.so/blob/main/tests/e2e-infra-market.rs#L159. Note the aggressive use of custom macros to support testing - development with Stylus (and Rust like this) is such a pleasure. Note everywhere the use of macros to guard accesses of balances and more. Super great!
Custom error handling shortcircuiting/panic handler with full stack trace depending on the backend without compromising on the no-std requirement (even with WASM!), under codesize https://github.com/fluidity-money/9lives.so/blob/main/src/error.rs - development with this practice means running code, that, when it reverts, simply states “fusdc contract sending to …” https://github.com/fluidity-money/9lives.so/blob/main/src/error.rs#L521. This is also facilitated with the
c!
macro https://github.com/fluidity-money/9lives.so/blob/1b8ecac79ab0d9487206b721fc58b6b4dfdbdb6b/src/error.rs#L35-L52. For the custom panic handler: https://github.com/fluidity-money/9lives.so/blob/main/src/main.rs#L9-L28 This is another great error reporting opportunity over the classic EVM stack: in-depth stack unwinding without invasive tooling.Custom interpreter for symbol exporting/debug eth call testing of EVM interactions without dropping into a bespoke e2e test based on the node (this is the subject of this post, so I won’t elaborate).

We hope developers can jump in and get a better idea of how to do development with Stylus using what we’ve written as a tool, and as such we’ve done our best to make it as inclusive as possible. If anyone has any questions about the codebase, please reach out to me as @doggish via Telegram (or @baygeeth via X)! We’ve licensed this with MIT, to support developers in building with Stylus. Though be warned: this is largely unaudited.
Before proceeding to regular programming, I’d like to thank the Fluidity Money and Superposition community (especially our early testers) for patiently waiting and providing us with feedback for the mainnet launch. Everyone who made a market will bag a neat chain-wide achievement (and the associated points).
Lit Protocol
Now for some regular programming, and to recap Stylus:
Arbitrum Stylus is a fantastic technology for building on the EVM: WASM-powered smart contracts that are orders of magnitude cheaper and more expressive than their Solidity counterparts. This means a much cheaper, safer, and more accessible Arbitrum for everyone! Stylus contracts are compatible with the EVM day 1: that also means no friction with the existing ecosystem.
Stylus Saturdays is an ongoing newsletter exploring the Stylus ecosystem, focusing on projects developing in the ecosystem, and technical tidbits to support adoption of Stylus.
Today’s post is the second in the format of 101 Stylus Nights. This will be a semi-regular technical digestible that hopefully will come in handy to people touching on Stylus for the first time. As the name implies, we’ll do 101 of these. Today’s episode is how to build a Stylus WASM forknet runner, which we alluded to in the previous bit of info about 9lives. This will enable us to test our code using the WASM backend, and to simulate how our contract would perform based on on-chain state. It also lets us test with side effects, like with a ERC20 asset, using a mixed combination of ETH RPC features. Hopefully this comes in handy for the plethora of submitters in the Stylus Sprint program, who will surely be rebuilding their versions of this. At Superposition, we have a interpreter based on REVM internally that we hope to release in the future that takes ideas from this runner.
Lit Protocol is building a decentralised signing network.
They’ve implemented user-friendly wallet on-ramping UX, and address-gated content, including tools to control access to content for decentralised AI. They’ve used Stylus to implement cryptography precompiles that they use for their on-chain verification.
One of the best uses of Stylus is implementing low cost primitives that can be used by other contracts, and Lit does just that. https://github.com/LIT-Protocol/lit-precompiles/tree/hwrdtm/modular-precompiles/src their code lives here, and they’re using external crates for cryptography, something only possible with Stylus! https://github.com/LIT-Protocol/lit-precompiles/blob/hwrdtm/modular-precompiles/src/core/src/k256.rs
This is feasible since the creation and activation of contracts is taking the WASM-produced contarct, and converting it to native code to be run on nodes. This way, you get native equivalent performance for free!
You can join their Discord here: https://discord.com/invite/zBw5hDZve8
Developer resources live here: https://developer.litprotocol.com/
Building a Stylus Forknet
This post will touch on some code we developed at Superposition that we hope will come in handy for other developers.
This tool is useful for taking scenarios like this:
And turning them into:
Or:
This is a lot more convenient than making heads or tails of revertdata.
How does it work?
Our Stylus forknet interpreter (known as stylus-interpreter
), living at https://github.com/fluidity-money/9lives.so/blob/main/tools/stylus-interpreter/src/main.rs, functions by simulating calls to a remote RPC host using the debug_* namespace. It then sets storage internally to give it back to the wasm host: https://github.com/fluidity-money/9lives.so/blob/main/tools/stylus-interpreter/src/main.rs#L440, and it supports some extra functions on top of what’s already available with Stylus: https://github.com/fluidity-money/9lives.so/blob/main/tools/stylus-interpreter/src/main.rs#L758-L805. It does this so that you can combine it with other tools in your codebase, like a custom panic handler, to get a full stack trace of when issues happen, instead of reverting.
This is pretty useful for developers who need a supplementary tool for figuring out why something broke during their other tests (like us). If something breaks with your contract, you can simply recompile your contract with a feature flag indicating it’s in a state where it should have the custom panic handler (ie, https://github.com/fluidity-money/9lives.so/blob/main/src/main.rs#L9-L15), and get in depth info as to why your code broke.
We intend to release a crate for this to simplify using this in the future. But first, let’s answer some questions.
How does debug call simulation work?
eth_call can be simulated with a variety of tracers using the eth debug namespace. We use it extensively elsewhere (see Gist of how you might use this normally with every tracer under the sun). Tracers are essentially code that’s run on your Geth (or equivalent) node that returns structured data aside from what you might normally receive. This is useful for understanding state changes in lieu of a plugin to the node itself.
We use the call and prestate tracer to simulate the contract call. The call tracer lets us get info on the circumstances of the call, including whether it worked. The prestate tracer lets us examine the before and after state of the contract, including storage slots.
In an internal use, this spits out a RPC request like the following:
The reply is quite substantial, so I won’t share it here. If you’re interested in seeing this yourself, I encourage you to check out the proxy-http Gist.
What this has essentially done is make a call on-chain, recorded info about the changes to any storage slots/code, then returned it to us.
Persisting the results of these calls to simulate the EVM
Following these calls, we unpack the response from the request. We unpack the reply to begin to persist the changes to the state.
We then unpack the reply from the calltracer, which told us whether we succeeded or failed. All of this was accomplished using alloy’s RPC features of course. Depending on whether we had a reply or not in the way of calldata, we write the return data pointer, assuming the pointer itself is hygienic and doesn’t need to be set if the reply is 0.
And thus, we conclude the circle of life, with the following commitment to a static variable: *LAST_CALL_CALLDATA.get().unwrap().lock().await = d.clone();
This can be then read using the hostio
functions return_data_size
, and read_return_data
to know how the call went, and the size of the returndata. https://github.com/fluidity-money/9lives.so/blob/main/tools/stylus-interpreter/src/main.rs#L632-L671
Any future calls to CREATE1
, and CALL
(at the time of writing, I haven’t found a solution without massively blowing out the lines of code to support static call
, delegate call
, create2
) will run with the state overrides that we learned about as a result of our previous calls https://github.com/fluidity-money/9lives.so/blob/main/tools/stylus-interpreter/src/main.rs#L282-L296. This way, we can simulate a faux EVM from the comfort of our own RPC, but with affordances to know how errors took place.
Storage
Storage accesses and persistence is the use of a key value storage of address * (word * word)
. If a storage miss takes place, then the RPC does a look up to get it for the user.
This way, we can have the locally running environment be based on the state variables of what’s based on-chain, despite writing taking place locally regardless:

We hope you use this somehow, and it saves you time! We will progressively release a crate that also supports the REVM backend for this work instead of developing with the simulation of eth debug.
We feel however, that this is a reasonable stand-in for a forknet!
Stylus resources
In no particular order:
And that’s it for this week!
If your project has any updates you’d like to share, or you would like to request that we monitor your project’s developments to include you in the weekly newsletter, please reach out to @baygeeth on X!
Stylus Saturdays is powered by Superposition, the first defi-native layer 3 that pays you to use it. Try out the incentivised testnet at