Welcome to another week of Stylus Saturdays! I’m experimenting with engagement with a biweekly format (and at different times, as you may have noticed). Arbitrum Stylus is a fantastic new 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 hugely 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, each week featuring a project developing on Stylus!
Today’s post will include the first of a regular series, 101 Stylus Nights. This will be a 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. For the first episode, we’ll explore employing the factory pattern with proxies, and how you might do so.
At Superposition, we’re super excited to be building with this platform and technology, and we want to share with the world this emerging ecosystem. Superposition is building the first defi-native chain that pays you to use it!
Week 4: The Stylus NFT
The first Stylus NFT has been made! Make sure to mint yours at https://www.mintstylus.xyz/. Who knows what kind of physical merch is inside!
101 Stylus Nights: Proxying Stylus
So you want to have multiple contracts deployed in your Stylus project! Maybe you’ve got a pool pattern similar to Uniswap V1-V3, or you’re working with a type of math that’s not so well known (and has risks you want to be certain you can account for), or you want to have upgrades without stressing storage collision!
Whatever the reason, a proxy contract is the way to do this, instead of redeploying large contract bytecode every time.
Side note: in the below examples, we’re blowing up the contract with a panic every time something goes wrong! It’s better to return data.
What’s the transparent upgradeable proxy pattern?
A quick review: what’s a transparent upgradeable proxy? Simply put, it’s an intermediary contract that routes calls using DELEGATECALL
to an implementation contract that contains its actual code. DELEGATECALL
copies the execution context to a target for execution.
It’s handing off everything to another contract, that then runs its own code on the calldata and storage input, but centralising the storage that was used in the execution to the other contract to the proxy contract. This is useful for upgrades.
Transparent upgradeable proxies have a path that supports upgrades that can be called by a trusted address. If the caller is not the trusted address, it falls back, and makes the call normally. This is overwhelmingly used by deployers for safe upgrades on one-off occasions.
Implementing a proxy using Stylus
The code might be as follows (this is implemented using Stylus, which as a reminder, is 1-to-1 equivalent with the EVM in terms of operations including storage use)[1]:
The idea is pretty simple. The implementation is not relatively speaking, as fallback functions are not natively supported in Stylus (and in the code above we reimplement the entrypoint to do so). Note to users that you would not write your code this way in practice! You would define an entrypoint using the macro, visibility on functions, and this function would be generated, with branching based on the selector builtin.
Using a proxy off the shelf during your build process
A programmer might simply use OpenZeppelin’s, like the way we do it. This is a script in Foundry Forge:
Other project tools like Hardhat let you use this quite easily. I prefer to roll a script that generates the bytecode on the fly. You could do this, leveraging Foundry Forge:
forge create \
--evm-version cancun \
--rpc-url "$url" \
"TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy" \
--constructor-args "$logic_addr" "$proxy_admin" "$data"
Though this specific approach is preferable to a deploying user, it might be preferable for a factory with a complicated setup ritual, maybe one that’s doing several trusted deployments following a logic deployment by its owner.
What’s a very small immutable proxy?
The minimal proxy pattern is a EIP-1167 proxy that’s quite small! It could be useful in a factory contract that does a ton of deploying! It’s available natively in some programming languages, including Vyper. You might use it in Solidity as such:
The minimal proxies are literally size optimised proxies identical to the previous proxy functionality-wise, though without the upgrade functionality.
This is how we leverage minimal proxies at Superposition for our upcoming prediction market. Upon creation of a campaign, a proxy is deployed for the “shares” representing tradeable investment in outcomes, and a host contract (trading contract) that centralises the liquidity. This lets us do 3 contract deployments, but we can proxy to them as much as we want! This is good for write only on new deployment style upgrading (say, gradually releasing new features, or code without restrictions that are gradually released).
This is not the EIP-specced minimal proxy, this is a Solady optimised one:
For the array concatenation, we use the macro array-concat. This is fun in that everything stored in the code, avoiding a SLOAD
. The proxy data is constructed using array concatenation at compile time. This lets us have a factory contract, with code reuse with the deployed contract, that simply and cheaply deploys multiple ERC20s (in our case, shares representing an outcome) upon creation of a prediction environment. This is how we can store EVM bytecode for a simple proxy!
What’s a beacon proxy?
A beacon proxy is similar to a transparent proxy, except a contract is deployed somewhere that returns the implementation address that immutable proxies call into when it’s asked. Where a transparent upgradeable proxy checks the caller and decides if it should upgrade itself depending on who they are, a beacon proxy asks a contract for the address, which might have its own upgrade semantics separate to the proxy code itself.
This is useful when you have, say, multiple derivatives of an asset that might include some behaviour you’ve built on top. This way you can deploy the beacon proxy and point it to your beacon, and easily keep everything up to date if you have to push out upgrades!
At Fluidity Money, we used this pattern for our Token contract, which is a pegged yield bearing asset. Revisiting the original implementation of the transparent upgradeable proxy, the code should look like this for the code that calls the beacon to get the implementation address:
As you can see, it’s a little shorter, and the beacon address is hardcoded. In Stylus, a way to have multiple contracts deployed in the same file, sharing the same code, is to use Rust feature flags. In the above, we use the feature contract-proxy
to compile this entrypoint.
This beacon code is something we’re more familiar with:
You can see that it simply returns the address of the implementation contract when asked using implementation
, and that it has a function (setLogic
) that can be used to set a new logic address if asked to do so.
Next episode of 101 Stylus Nights: How to link cryptography libraries, and deploy them on-chain!
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. Keep your eyes peeled for mainnet!
[1] I’d like to note that it can be nonsensical to use Stylus for operations where compute isn’t taking place. This might include in a proxy, where you don’t want to pay the inevitable warmup cost to utilise the Stylus VM, where EVM code could be used instead.