<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Lutra Labs</title>
        <link>https://paragraph.com/@mirror.lutralabs</link>
        <description>A deep tech R&amp;D studio specializing in blockchain infrastructure, Web3, distributed systems, cryptography, and machine learning.</description>
        <lastBuildDate>Fri, 17 Apr 2026 01:44:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Lutra Labs</title>
            <url>https://storage.googleapis.com/papyrus_images/66aa5b8978a73dc689f9264c2ccc2f269f5fd5b7824bbd0a2f94eb73ab61ab56.png</url>
            <link>https://paragraph.com/@mirror.lutralabs</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Automating Liquidations — A Deep Dive into the Compound V3 Architecture Liquidator 💸]]></title>
            <link>https://paragraph.com/@mirror.lutralabs/automating-liquidations-a-deep-dive-into-the-compound-v3-architecture-liquidator</link>
            <guid>QERPiY5ayspvCmdKEBVV</guid>
            <pubDate>Mon, 27 Jan 2025 17:35:42 GMT</pubDate>
            <description><![CDATA[IntroductionWith lending protocols like Compound V3 and its derivatives, such as Swaylend (a top lending protocol on Fuel Network), a liquidation happens when a borrower&apos;s collateral value drops below a certain threshold compared to their borrowed amount. In such cases, the user must be liquidated. Anyone can call the liquidation method; it is not limited to the governor. Liquidations are necessary to maintain a healthy market. They ensure that all debts are repaid appropriately and help...]]></description>
            <content:encoded><![CDATA[<h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>With lending protocols like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://compound.finance/"><strong>Compound V3</strong></a> and its derivatives, such as <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://swaylend.com"><strong>Swaylend</strong></a> (a top lending protocol on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://fuel.network/">Fuel Network</a>), a liquidation happens when a borrower&apos;s collateral value drops below a certain threshold compared to their borrowed amount. In such cases, the user must be liquidated. Anyone can call the liquidation method; it is not limited to the governor. Liquidations are necessary to maintain a healthy market. They ensure that all debts are repaid appropriately and help prevent losses for both the protocol and its users.</p><p>With that purpose, instead of liquidating users manually, automated bots come in handy.</p><h2 id="h-steps" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Steps</h2><p>We roughly divide the liquidation process into three main steps:</p><ol><li><p>Get and liquidate insolvent users</p></li><li><p>Buy off protocol&apos;s collateral</p></li><li><p>Sell collateral</p></li></ol><p>The steps are explained in <em>How to</em> below.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/40acbe269747578eecf3562709397cf8952c6f228bdda34f44b8486de1faad14.png" alt="Liquidator&apos;s flowchart" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Liquidator&apos;s flowchart</figcaption></figure><h2 id="h-relevant-contract-methods" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Relevant contract methods</h2><p>Let’s take a look at <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Swaylend/swaylend-monorepo/blob/develop/contracts/market/src/main.sw">Swaylend’s</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/compound-finance/comet/blob/main/contracts/Comet.sol">Compound’s</a> contracts, which are written in <strong>Sway</strong> and <strong>Solidity,</strong> respectively. One runs on FuelVM, while the other operates on the traditional EVM. The most relevant methods are listed below.</p><ol><li><p><strong>Absorbing users</strong></p><pre data-type="codeBlock" text="// Sway
fn absorb(accounts: Vec&lt;Identity&gt;, price_data_update: PriceDataUpdate);

// Solidity
function absorb(address absorber, address[] calldata accounts) override external;
"><code><span class="hljs-comment">// Sway</span>
fn absorb(accounts: Vec<span class="hljs-operator">&#x3C;</span>Identity<span class="hljs-operator">></span>, price_data_update: PriceDataUpdate);

<span class="hljs-comment">// Solidity</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">absorb</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> absorber, <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> accounts</span>) <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
</code></pre><p>This method liquidates users passed as the <code>accounts</code> parameter when called.</p></li><li><p><strong>Getting collateral quotes</strong></p><pre data-type="codeBlock" text="// Sway
fn collateral_value_to_sell(asset_id: AssetId, collateral_amount: u64) -&gt; u64;

// Solidity
function absorb(address absorber, address[] calldata accounts) override external;
"><code><span class="hljs-comment">// Sway</span>
fn collateral_value_to_sell(asset_id: AssetId, collateral_amount: u64) <span class="hljs-operator">-</span><span class="hljs-operator">></span> u64;

<span class="hljs-comment">// Solidity</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">absorb</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> absorber, <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> accounts</span>) <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
</code></pre><p>Calculates the amount of base asset to send along with the <code>buy_collateral</code> call to buy the desired amount of the collateral asset.</p></li><li><p><strong>Buying collateral</strong></p><pre data-type="codeBlock" text="// Sway
fn buy_collateral(asset_id: AssetId, min_amount: u64, recipient: Identity);

// Solidity
function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) override external nonReentrant;
"><code><span class="hljs-comment">// Sway</span>
fn buy_collateral(asset_id: AssetId, min_amount: u64, recipient: Identity);

<span class="hljs-comment">// Solidity</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buyCollateral</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> asset, <span class="hljs-keyword">uint</span> minAmount, <span class="hljs-keyword">uint</span> baseAmount, <span class="hljs-keyword">address</span> recipient</span>) <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">nonReentrant</span></span>;
</code></pre><p>Buys collateral for the amount of base asset passed along with the function and receives, at minimum, the passed <code>min_amount</code>.</p></li></ol><h2 id="h-how-to" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How to</h2><h3 id="h-liquidate-users" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Liquidate users</h3><p><strong>1. Getting and updating configurations</strong></p><p>First up, we need to get the latest market configurations to ensure we are working with the latest data.</p><pre data-type="codeBlock" text="marketConfig := marketContract.getMarketConfiguration()
collateralConfig := marketContract.getCollateralConfiguration()
marketBasics := marketContract.getMarketBasics()

liquidator.updateConfigs(marketConfig, collateralConfig, marketBasics)
"><code>marketConfig :<span class="hljs-operator">=</span> marketContract.getMarketConfiguration()
collateralConfig :<span class="hljs-operator">=</span> marketContract.getCollateralConfiguration()
marketBasics :<span class="hljs-operator">=</span> marketContract.getMarketBasics()

liquidator.updateConfigs(marketConfig, collateralConfig, marketBasics)
</code></pre><p><strong>2. Finding liquidatable users</strong></p><p>Next, we first need to get users with negative principals and calculate the insolvent ones, ready to go underwater.</p><pre data-type="codeBlock" text="usersWithNegativePrincipal := indexer.fetchUsersWithNegativePrincipals()
insolventUsers := []

for each user in usersWithNegativePrincipal
  if isInsolvent(user)
    append user to insolventUsers
"><code>usersWithNegativePrincipal :<span class="hljs-operator">=</span> indexer.fetchUsersWithNegativePrincipals()
insolventUsers :<span class="hljs-operator">=</span> []

<span class="hljs-keyword">for</span> <span class="hljs-keyword">each</span> <span class="hljs-keyword">user</span> <span class="hljs-keyword">in</span> usersWithNegativePrincipal
  if isInsolvent(<span class="hljs-keyword">user</span>)
    append <span class="hljs-keyword">user</span> <span class="hljs-keyword">to</span> insolventUsers
</code></pre><p>Under the hood, we can use indexers like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://thegraph.com/">The Graph</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://envio.dev/">Envio</a> to index the market contract and to get users with negative principals — users currently borrowing from our lending protocol. All calculations are then made offchain by using an adapted version of market contract&apos;s function <code>is_liquidatable</code> — be found <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Swaylend/swaylend-monorepo/blob/8455a9f5f5d40e79f63260476aeaa8893829f37e/contracts/market/src/main.sw#L786">here</a>. Alternatively, instead of calculating users&apos; insolvency off-chain, we can call the <code>is_liquidatable</code> method for each user. However, this approach is less efficient.</p><p><strong>3. Absorbing users</strong></p><p>The users we get from step 3 are then passed into the <code>absorb</code> call to liquidate underwater accounts.</p><pre data-type="codeBlock" text="marketContract.absorb(insolventUsers)
"><code>marketContract.absorb(insolventUsers)
</code></pre><blockquote><p><strong>Note</strong></p><p>The fees are not covered by the liquidator; they are paid by the protocol.</p></blockquote><h3 id="h-buy-collateral-assets" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Buy collateral assets</h3><p><strong>1. Updating collateral reserves</strong></p><p>First, we need to update the market’s collateral reserves in the liquidator&apos;s internal storage.</p><pre data-type="codeBlock" text="collaterals := marketContract.getCollateralConfigurations()

for each collateral in collaterals
  config := marketContract.getCollateralReservers(collateral.id)
  updateCollateralReserves(collateral.id, config)
"><code>collaterals :<span class="hljs-operator">=</span> marketContract.getCollateralConfigurations()

<span class="hljs-keyword">for</span> each collateral in collaterals
  config :<span class="hljs-operator">=</span> marketContract.getCollateralReservers(collateral.id)
  updateCollateralReserves(collateral.id, config)
</code></pre><p><strong>2. Updating collateral values</strong></p><p>Next, we update the actual value of the collateral reserves by iterating through the previously updated reserves, fetching the price from oracle for each asset, and storing it in liquidator’s internal storage.</p><pre data-type="codeBlock" text="collaterals := getCollateralConfigurationsFromInternalStorage()
for each collateral in collaterals
  price := oracle.getPrice(collateral.id)
  updateCollateralPrices(collateral.id, price)
"><code>collaterals :<span class="hljs-operator">=</span> getCollateralConfigurationsFromInternalStorage()
<span class="hljs-keyword">for</span> each collateral in collaterals
  price :<span class="hljs-operator">=</span> oracle.getPrice(collateral.id)
  updateCollateralPrices(collateral.id, price)
</code></pre><p><strong>3. Building and executing buy transactions</strong></p><p>With the collateral amount and value now stored in internal storage, we can begin buying the collateral. In this step, we can set any limitations we want, such as buying only specific collateral assets or avoiding buying if the value does not exceed a specific threshold. For simplicity, we’ll leave those configurations and algorithms for building transactions to optimize your return to you.</p><pre data-type="codeBlock" text="collaterals := getCollateralsToBuy()
for each collateral in collaterals
  amountToBuy := calculateAmountToBuy(collateral)
  marketContract.buyCollateral(collateral.id, amountToBuy)
"><code>collaterals :<span class="hljs-operator">=</span> getCollateralsToBuy()
<span class="hljs-keyword">for</span> each collateral in collaterals
  amountToBuy :<span class="hljs-operator">=</span> calculateAmountToBuy(collateral)
  marketContract.buyCollateral(collateral.id, amountToBuy)
</code></pre><blockquote><p><strong>Note</strong></p><p>An important step in building transactions is calculating the price of each collateral amount we buy from the protocol. These collaterals are sold at a discount to incentivize users to repay the debts of liquidated users in base assets. The amount of collateral to sell to a user is based on the base asset the user sent with the transaction. This amount is either fetched from the contract or calculated off-chain. For example, you can find out how Swaylend performs this calculation <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Swaylend/swaylend-monorepo/blob/8455a9f5f5d40e79f63260476aeaa8893829f37e/contracts/market/src/main.sw#L878">here</a>.</p></blockquote><h3 id="h-sell-collateral-assets-for-profit" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Sell collateral assets for profit</h3><p>Now that we hold the collateral value in the Liquidator’s wallet, we can decide whether to sell or hold it. We leave this decision up to you. There are many options for how to approach this step. A simple example would be to check which DEX offers the best price, as shown in the pseudocode below.</p><pre data-type="codeBlock" text="collaterals := getCollateralConfigurationsFromInternalStorage()
baseAssetId := marketConfig.baseAssetId

for each collateral in collaterals
  uniswapPrice := uniswapContract.getPrice(collateral.id)
  balancerPrice := balancerContract.getPrice(collateral.id)
  balance := wallet.getBalance(collateral.id)
  dexContract := null

  if uniswapPrice &gt; balancerPrice
    dexContract := uniswapContract
  else
    dexContract := balancerContract

  poolId := dexContract.getPoolId(baseAssetId, collateral.id)
  deadline := now() + 10000

  dexContract.swap(
    collateral.amountToBuy,
    collateral.id,
    poolId,
    deadline
  )
"><code>collaterals :<span class="hljs-operator">=</span> getCollateralConfigurationsFromInternalStorage()
baseAssetId :<span class="hljs-operator">=</span> marketConfig.baseAssetId

<span class="hljs-keyword">for</span> each collateral in collaterals
  uniswapPrice :<span class="hljs-operator">=</span> uniswapContract.getPrice(collateral.id)
  balancerPrice :<span class="hljs-operator">=</span> balancerContract.getPrice(collateral.id)
  balance :<span class="hljs-operator">=</span> wallet.getBalance(collateral.id)
  dexContract :<span class="hljs-operator">=</span> null

  <span class="hljs-keyword">if</span> uniswapPrice <span class="hljs-operator">></span> balancerPrice
    dexContract :<span class="hljs-operator">=</span> uniswapContract
  <span class="hljs-keyword">else</span>
    dexContract :<span class="hljs-operator">=</span> balancerContract

  poolId :<span class="hljs-operator">=</span> dexContract.getPoolId(baseAssetId, collateral.id)
  deadline :<span class="hljs-operator">=</span> <span class="hljs-built_in">now</span>() <span class="hljs-operator">+</span> <span class="hljs-number">10000</span>

  dexContract.swap(
    collateral.amountToBuy,
    collateral.id,
    poolId,
    deadline
  )
</code></pre><h2 id="h-future-improvements" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Future improvements</h2><p>Future improvements could focus on optimizing transaction-building efficiency and the liquidator&apos;s performance. This includes optimizing the processes for building buy and sell transactions, storing transaction history, and the liquidator&apos;s overall profit realized. Additionally, implementing parallel execution of all main steps can significantly boost the bot&apos;s performance when other market makers arrive, and checking more DEXes can help identify the best platforms for selling the acquired collateral to maximize profit.</p><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>A liquidator automates identifying and liquidating insolvent users, purchasing discounted collateral waiting in the protocol, and immediately selling it for profit. The bot aims to run at optimal performance and profitability by utilizing advanced transaction-building strategies.</p><hr><h3 id="h-by-lutra-labs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>By Lutra Labs</strong></h3><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://lutralabs.io/">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.gg/txGKFpBxtc">Discord</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.linkedin.com/company/lutras">LinkedIn</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/lutralabs_">Twitter/X</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lutralabs">GitHub</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mailto:info@lutralabs.io">E-Mail</a></p><p><em>Written by Urban Vidovič, COO</em></p>]]></content:encoded>
            <author>mirror.lutralabs@newsletter.paragraph.com (Lutra Labs)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/0c22a1f30090f412b2f1cfffdb2c90a4a56978ce509411aa910deebbfd02cf45.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Journey with Arbitrum Stylus — Tackling Advent of Code in Rust 🦀]]></title>
            <link>https://paragraph.com/@mirror.lutralabs/journey-with-arbitrum-stylus-tackling-advent-of-code-in-rust</link>
            <guid>kUTSgGJoCFFnzoRlTiLw</guid>
            <pubDate>Tue, 21 Jan 2025 11:18:34 GMT</pubDate>
            <description><![CDATA[IntroductionAs a blockchain developer experienced in building across multiple chains and working with various smart contract languages, I’ve been captivated by the ecosystem’s growing embrace of Rust and Rust-inspired languages. From Solana’s native Rust support to emerging languages like Sway on Fuel, Move on Sui and Aptos, and Fe on Ethereum, a clear trend is taking shape—and it’s wearing a crab badge. 🦀 This shift highlights Rust’s rising dominance in the blockchain space. Major platforms...]]></description>
            <content:encoded><![CDATA[<h1 id="h-introduction" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h1><p>As a blockchain developer experienced in building across multiple chains and working with various smart contract languages, I’ve been captivated by the ecosystem’s growing embrace of Rust and Rust-inspired languages. From Solana’s native Rust support to emerging languages like Sway on Fuel, Move on Sui and Aptos, and Fe on Ethereum, a clear trend is taking shape—and it’s wearing a crab badge. 🦀</p><p>This shift highlights Rust’s rising dominance in the blockchain space. Major platforms have chosen Rust for their core infrastructure, benefiting from its performance, robust memory safety guarantees, and zero-cost abstractions. The abundance of skilled Rust developers has further fueled this adoption. Rust is being used for both core infrastructure and smart contracts in new projects, and established chains are increasingly looking to add support for it to strengthen their ecosystems.</p><p>And this is where Stylus comes in—it eliminates the need for Rust-inspired approximations and allows you to build directly with Rust itself.</p><h2 id="h-what-is-stylus" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What is Stylus ?</h2><p>Stylus is an upgrade to the Arbitrum Nitro tech stack, adding a second virtual machine that runs WebAssembly (WASM) instead of the Ethereum Virtual Machine (EVM). This allows developers to write smart contracts in languages like Rust, C, and C++, which compile to WASM. These languages are more efficient for compute-heavy tasks, resulting in faster execution and lower gas fees compared to Solidity. Developers can take advantage of existing libraries and tools from these languages to build smart contracts with greater efficiency.</p><p>Stylus is fully compatible with Solidity, meaning Solidity and WASM contracts can interact with each other. Developers can use Stylus to optimize specific parts of an application by replacing only the compute-heavy components with WASM contracts, while the rest of the application can remain in Solidity. This allows for fine-tuned performance improvements without having to rewrite entire dApps. Stylus is especially beneficial for memory and computation-intensive applications, such as complex DeFi protocols, zero-knowledge proofs and on-chain games.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3bebf7bda90ed628cbb74ba2748400bd7b2f1047e2c6e608d60033fc1918856a.png" alt="The MultiVM paradigm (Image source: https://docs.arbitrum.io/stylus/gentle-introduction)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The MultiVM paradigm (Image source: https://docs.arbitrum.io/stylus/gentle-introduction)</figcaption></figure><h1 id="h-christmas-coding-stylus-advent-of-code" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Christmas Coding: Stylus × Advent of Code</h1><p>To put Stylus through its paces, I decided to implement solutions to Advent of Code challenges as smart contracts. Not because the problems demand blockchain (they definitely don&apos;t 😄), but because coding challenges provide an excellent testbed for exploring a new technology&apos;s capabilities, limitations, and performance characteristics.</p><p>In case you haven’t heard of it, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://adventofcode.com/2024/about">Advent of Code</a> is an annual series of coding puzzles released each December. It’s a fun way to challenge yourself and improve your problem-solving skills. The code for this project is open source and available on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lutralabs/advent-of-code-2024-smart-contracts-stylus">GitHub</a>.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/172236e3313698cb97b224c18f29cd96953fdb435c65ed3b2235cec5162c1a6b.gif" alt="Complete Advent of Code dashboard" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Complete Advent of Code dashboard</figcaption></figure><h2 id="h-setting-up-a-stylus-development-environment" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Setting Up a Stylus Development Environment</h2><p>Getting started with Stylus development required several tools, most of which I already had in my toolkit: the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.rust-lang.org/tools/install">Rust toolchain</a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.docker.com/">Docker</a>, and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/foundry-rs/foundry">Foundry</a>. The only new component I needed was the Nitro devnode, which I set up using the scripts from the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/OffchainLabs/nitro-devnode">nitro-devnode repository</a>.</p><p>With the environment ready, I installed cargo-stylus using <code>cargo install cargo-stylus</code> and created my first project with <code>cargo stylus new &lt;PROJECT_NAME&gt;</code>. This generated a project based on the Stylus hello world template, which implements Foundry&apos;s Counter example. I verified everything was working correctly by testing essential commands:</p><ul><li><p><code>cargo stylus check</code> for dry-running the project, compiling to WASM, and verifying deployability</p></li><li><p><code>cargo stylus export-abi</code> for generating the contract&apos;s ABI</p></li><li><p><code>cargo stylus deploy</code> for deployment</p></li></ul><p>I successfully verified the deployed contracts by calling them using Foundry cast.</p><h2 id="h-overcoming-testing-and-monorepo-challenges" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Overcoming Testing and Monorepo Challenges</h2><p>While the initial setup went smoothly, I encountered some interesting challenges when implementing tests and converting the project to a monorepo structure.</p><p>For testing, I utilized TucksonDev&apos;s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/TucksonDev/e2e-lib">e2e-lib</a>, a simplified fork of OpenZeppelin&apos;s e2e library designed for testing Stylus contracts. The first issue I encountered involved the <code>console!()</code> macro, which wasn’t working during tests. Strangely, it worked fine when deploying contracts and interacting with them through Foundry cast, showing logs in the sequencer’s Docker container. The solution was straightforward once discovered: I needed to enable the debug feature in cargo dependencies by updating <code>stylus-sdk = &quot;0.6.0&quot;</code> to <code>stylus-sdk = { version = &quot;0.6.0&quot;, features = [&quot;debug&quot;] }</code>.</p><p>The transition to a monorepo setup involved creating a Cargo workspace and organizing files. While this was relatively straightforward, both the <code>cargo stylus check</code> and <code>cargo stylus deploy</code> commands started throwing errors. I found inspiration in solving the issues in OpenZeppelin&apos;s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/OpenZeppelin/rust-contracts-stylus/tree/main/scripts">Rust Contract repository</a>, adapting their approach to create custom scripts for handling specific WASM files. Here&apos;s the resulting <code>check-wasm.sh</code> script:</p><pre data-type="codeBlock" text="#!/bin/bash

# Adapted code from `OpenZeppelin/rust-contracts-stylus`
# Ref: https://github.com/OpenZeppelin/rust-contracts-stylus/blob/main/scripts/check-wasm.sh

# Function for checking contract wasm binary by crate name
check_wasm () {
  local CONTRACT_CRATE_NAME=$1
  local CONTRACT_BIN_NAME=&quot;${CONTRACT_CRATE_NAME//-/_}.wasm&quot;
  
  echo
  echo &quot;Checking contract $CONTRACT_CRATE_NAME&quot;
  cargo stylus check --wasm-file ./target/wasm32-unknown-unknown/release/&quot;$CONTRACT_BIN_NAME&quot;
}

# Retrieve all alphanumeric contract&apos;s crate names in `./contracts/solutions` directory.
get_solution_crate_names () {
  find ./contracts/solutions -maxdepth 2 -type f -name &quot;Cargo.toml&quot; | xargs grep &apos;name = &apos; | grep -oE &apos;&quot;.*&quot;&apos; | tr -d &quot;&apos;\&quot;&quot;
}

# Build all projects
cargo build --release --target wasm32-unknown-unknown

# Ceck all projects
for CRATE_NAME in $(get_solution_crate_names)
do
  check_wasm &quot;$CRATE_NAME&quot;
done
"><code>#<span class="hljs-operator">!</span><span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>bash

# Adapted code <span class="hljs-keyword">from</span> `OpenZeppelin<span class="hljs-operator">/</span>rust<span class="hljs-operator">-</span>contracts<span class="hljs-operator">-</span>stylus`
# Ref: https:<span class="hljs-comment">//github.com/OpenZeppelin/rust-contracts-stylus/blob/main/scripts/check-wasm.sh</span>

# Function <span class="hljs-keyword">for</span> checking <span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">wasm</span> <span class="hljs-title">binary</span> <span class="hljs-title">by</span> <span class="hljs-title">crate</span> <span class="hljs-title">name</span>
<span class="hljs-title">check_wasm</span> (<span class="hljs-params"></span>) </span>{
  local CONTRACT_CRATE_NAME<span class="hljs-operator">=</span>$1
  local CONTRACT_BIN_NAME<span class="hljs-operator">=</span><span class="hljs-string">"${CONTRACT_CRATE_NAME//-/_}.wasm"</span>
  
  echo
  echo <span class="hljs-string">"Checking contract $CONTRACT_CRATE_NAME"</span>
  cargo stylus check <span class="hljs-operator">-</span><span class="hljs-operator">-</span>wasm<span class="hljs-operator">-</span>file ./target<span class="hljs-operator">/</span>wasm32<span class="hljs-operator">-</span>unknown<span class="hljs-operator">-</span>unknown<span class="hljs-operator">/</span>release<span class="hljs-operator">/</span><span class="hljs-string">"$CONTRACT_BIN_NAME"</span>
}

# Retrieve all alphanumeric <span class="hljs-class"><span class="hljs-keyword">contract</span>'<span class="hljs-title">s</span> <span class="hljs-title">crate</span> <span class="hljs-title">names</span> <span class="hljs-title">in</span> `./<span class="hljs-title">contracts</span>/<span class="hljs-title">solutions</span>` <span class="hljs-title">directory</span>.
<span class="hljs-title">get_solution_crate_names</span> (<span class="hljs-params"></span>) </span>{
  find ./contracts<span class="hljs-operator">/</span>solutions <span class="hljs-operator">-</span>maxdepth <span class="hljs-number">2</span> <span class="hljs-operator">-</span><span class="hljs-keyword">type</span> f <span class="hljs-operator">-</span>name <span class="hljs-string">"Cargo.toml"</span> <span class="hljs-operator">|</span> xargs grep <span class="hljs-string">'name = '</span> <span class="hljs-operator">|</span> grep <span class="hljs-operator">-</span>oE <span class="hljs-string">'".*"'</span> <span class="hljs-operator">|</span> tr <span class="hljs-operator">-</span>d <span class="hljs-string">"'\""</span>
}

# Build all projects
cargo build <span class="hljs-operator">-</span><span class="hljs-operator">-</span>release <span class="hljs-operator">-</span><span class="hljs-operator">-</span>target wasm32<span class="hljs-operator">-</span>unknown<span class="hljs-operator">-</span>unknown

# Ceck all projects
<span class="hljs-keyword">for</span> CRATE_NAME in $(get_solution_crate_names)
do
  check_wasm <span class="hljs-string">"$CRATE_NAME"</span>
done
</code></pre><h2 id="h-crafting-the-implementation" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Crafting the Implementation</h2><p>Initially, I started with a single smart contract containing all the Advent of Code solutions. However, by Day 5, the compressed size of the WASM smart contract exceeded the 24KB limit. To address this, I migrated to a more modular architecture and optimized contract size by:</p><ul><li><p>Using <code>#![no_std]</code> and instead importing needed data structures from external crates like <code>alloc</code> and <code>hashbrown</code>.</p></li><li><p>Split solutions into separate files, with each file dedicated to solving its own specific puzzle</p></li></ul><p>The final architecture consists of three main components:</p><ol><li><p><strong>An orchestrator smart contract:</strong></p><p>Acts as a proxy, tracking the addresses of deployed solution contracts for each day and routing calls appropriately. It also manages ownership and initialization status. Here&apos;s its storage structure:</p><pre data-type="codeBlock" text="#[storage]
#[entrypoint]
pub struct Orchestrator {
    initialized: StorageBool,
    owner: StorageAddress,
    day_to_solution: StorageMap&lt;u32, StorageAddress&gt;,
}
"><code><span class="hljs-meta">#[storage]</span>
<span class="hljs-meta">#[entrypoint]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Orchestrator</span> {
    initialized: StorageBool,
    owner: StorageAddress,
    day_to_solution: StorageMap&#x3C;<span class="hljs-type">u32</span>, StorageAddress>,
}
</code></pre><p>The orchestrator&apos;s key function is <code>solve</code>, which handles solving specific Advent of Code puzzles. Users provide the day, part, and input data, and the orchestrator determines the correct contract and method to invoke.</p><pre data-type="codeBlock" text="pub fn solve(&amp;mut self, day: u32, part: u32, input: String) -&gt; i64
"><code><span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">solve</span>(&#x26;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, day: <span class="hljs-type">u32</span>, part: <span class="hljs-type">u32</span>, input: <span class="hljs-type">String</span>) <span class="hljs-punctuation">-></span> <span class="hljs-type">i64</span>
</code></pre></li><li><p><strong>A trait (or interface)</strong>:</p><p>Defines the external API for solution contracts.</p><pre data-type="codeBlock" text="pub trait Solution {
    fn solvepart1(&amp;self, input: String) -&gt; i64;
    fn solvepart2(&amp;self, input: String) -&gt; i64;
}
"><code><span class="hljs-keyword">pub</span> <span class="hljs-keyword">trait</span> <span class="hljs-title class_">Solution</span> {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">solvepart1</span>(&#x26;<span class="hljs-keyword">self</span>, input: <span class="hljs-type">String</span>) <span class="hljs-punctuation">-></span> <span class="hljs-type">i64</span>;
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">solvepart2</span>(&#x26;<span class="hljs-keyword">self</span>, input: <span class="hljs-type">String</span>) <span class="hljs-punctuation">-></span> <span class="hljs-type">i64</span>;
}
</code></pre></li><li><p><strong>One smart contract per puzzle</strong>:</p><p>Each solution contract is minimal, with no storage and two external methods:</p><ul><li><p><code>solvepart_1</code></p></li><li><p><code>solvepart_2</code></p></li></ul><p>Each method corresponds to one of the two parts of an Advent of Code puzzle.</p></li></ol><p>This architecture allowed for scalability and ensured that each puzzle solution remained within the size constraints. The modular approach improved maintainability, making it easier to debug and update individual solutions without affecting others. Perhaps most importantly, it enabled independent testing and deployment of specific solutions, eliminating the need to redeploy everything when making changes to a single puzzle&apos;s implementation. Solutions to all puzzles can be found <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lutralabs/advent-of-code-2024-smart-contracts-stylus/tree/main/contracts/solutions">here</a>.</p><h2 id="h-other-notable-observations" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Other Notable Observations</h2><p>Throughout the development process, I made the following noteworthy observations:</p><p><strong>1. Large Binary Sizes:</strong> Some external crates, like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/rust-lang/regex">regex</a>, can significantly inflate the compiled WASM size. Adding regex pushed my contract size over 100KB, far exceeding the 24KB limit. This shows the importance of carefully choosing lightweight dependencies or creating custom implementations when needed.</p><p><strong>2. Gas Limit Issues:</strong> Code that runs fast in native Rust can still exceed the gas limit in smart contracts. Profiling and debugging are essential to identify gas-heavy operations and optimize the most expensive parts, including not just computationally intensive tasks but also memory-intensive ones, to stay within the limits.</p><h1 id="h-findings" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Findings</h1><p>Using Arbitrum Stylus to solve Advent of Code challenges proved to be an excellent learning experience. While using smart contracts for solving coding puzzles might seem unconventional and overkill, it helped me explore both Stylus&apos;s capabilities and its current limitations. The project highlighted important considerations for Rust smart contract development, from managing contract sizes to optimizing for gas efficiency. As Stylus continues to mature, it offers an exciting option for developers looking to build compute-intensive applications on Arbitrum using Rust&apos;s powerful capabilities.</p><h2 id="h-links" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Links</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lutralabs/advent-of-code-2024-smart-contracts-stylus">Repository</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://aoc2024.skippy-ai.com/">Website</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://adventofcode.com/2024/about">Advent of Code</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://arbitrum.io/stylus">Arbitrum stylus</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.arbitrum.io/stylus/gentle-introduction">Arbitrum docs</a></p></li></ul><hr><h3 id="h-by-lutra-labs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">By Lutra Labs</h3><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://lutralabs.io">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.gg/txGKFpBxtc">Discord</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.linkedin.com/company/lutras">LinkedIn</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/lutralabs_">Twitter/X</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lutralabs">GitHub</a> | E-Mail</p><p><em>Written by Martin Domajnko, CTO</em></p>]]></content:encoded>
            <author>mirror.lutralabs@newsletter.paragraph.com (Lutra Labs)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/f35bbca06757204d9a2d9356408f00e01e255ca08a4d1e6f5fb6ee555c78065a.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Introducing Lutra Labs — Building the Web3 Future 🦦 ]]></title>
            <link>https://paragraph.com/@mirror.lutralabs/introducing-lutra-labs-building-the-web3-future</link>
            <guid>zYJZrZzLMjGL67jQVJTO</guid>
            <pubDate>Tue, 14 Jan 2025 15:06:43 GMT</pubDate>
            <description><![CDATA[State of Web3Now is the perfect time to build in Web3, with the Ethereum ecosystem maturing and expanding as a solid foundation for innovation. The introduction of MICA regulations and growing government clarity provide a more secure and predictable environment for developers. Decentralized networks for storage, computation, and AI are emerging, unlocking new possibilities. This is the ideal moment to create applications that offer unique advantages over Web2 and build solutions that wouldn’t...]]></description>
            <content:encoded><![CDATA[<h2 id="h-state-of-web3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">State of Web3</h2><p>Now is the perfect time to build in <strong>Web3</strong>, with the <strong>Ethereum</strong> ecosystem maturing and expanding as a solid foundation for innovation. The introduction of <strong>MICA</strong> regulations and growing government clarity provide a more secure and predictable environment for developers. Decentralized networks for <strong>storage</strong>, <strong>computation</strong>, and <strong>AI</strong> are emerging, unlocking new possibilities. This is the ideal moment to create applications that offer unique advantages over Web2 and build solutions that wouldn’t be possible otherwise.</p><h2 id="h-who-we-are" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Who we are?</h2><p><strong>Lutra Labs</strong> is a deep-tech R&amp;D venture studio specializing in delivering cutting-edge products to the masses. We collaborate with various partners, helping bring their visions and products to life while simultaneously developing our own innovative solutions.</p><p>Our core expertise lies in everything Web3 (we&apos;ve been working in the space for 5+ years!), from infrastructure and application layers to smart contracts and system administration. However, as a team that continuously explores new technologies and endeavors, we also have expertise in machine learning, general software development, and the latest advancements in programming and computer technologies.</p><h2 id="h-portfolio" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Portfolio</h2><p>Some of the projects we&apos;ve been working on:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://masca.io"><strong>Masca</strong></a>: A decentralized identity wallet that allows you to store your digital identity and verifiable documents in an intuitive and simple way. Developed as a MetaMask snap, it integrates seamlessly with one of the most widely adopted Web3 wallets.</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://endorse.fun"><strong>endorse.fun</strong></a>: A reputation protocol where you can endorse and tip people and Web3 creators. Find users through their <strong>Farcaster</strong>, <strong>Lens</strong>, or <strong>ENS</strong> handles.</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/silius-rs/silius"><strong>Silius</strong></a>: Account abstraction is a key piece missing from broader Web3 adoption, enabling users to customize their blockchain accounts. Silius is an <strong>ERC-4337</strong> bundler, a critical low-level infrastructure component in the - Ethereum account abstraction ecosystem.</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://swaylend.com"><strong>Swaylend</strong></a>: As developer contributors, we helped build one of the largest lending protocols in DeFi on the emerging Layer 2 — <strong>Fuel Ignition</strong>.</p></li></ul><h2 id="h-team-and-background" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Team and background</h2><p>We are a team of five tech enthusiasts with vast expertise in development (of all kinds — just tell us what you need), research, and product management. As a spin-off from the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/blockchain-lab-um"><strong>Blockchain Lab:UM</strong></a>, we have strong connections within the academic sphere, which allows us to tackle and solve the most complex and challenging problems.</p><h2 id="h-closing-thoughts" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Closing thoughts</h2><p>That&apos;s it, folks! But only from our side... Have a project in mind? Want to brainstorm about the topics we mentioned in this blog post? <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://tally.so/r/woRglx">Contact us</a>, and let’s embark on the journey together!</p><hr><h3 id="h-by-lutra-labs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">By Lutra Labs</h3><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://lutralabs.io">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.gg/txGKFpBxtc">Discord</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.linkedin.com/company/lutras">LinkedIn</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/lutralabs_">Twitter/X</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lutralabs">GitHub</a> | E-Mail</p>]]></content:encoded>
            <author>mirror.lutralabs@newsletter.paragraph.com (Lutra Labs)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/7c42569f28b9c1fe65f9b35e0d77ae1ea5bc94ce356f54b5370c819744c18552.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>