<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Degen Code]]></title><description><![CDATA[The complete guide to building Ethereum trading & arbitrage bots.]]></description><link>https://www.degencode.com</link><image><url>https://substackcdn.com/image/fetch/$s_!G6XT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png</url><title>Degen Code</title><link>https://www.degencode.com</link></image><generator>Substack</generator><lastBuildDate>Thu, 30 Apr 2026 09:56:46 GMT</lastBuildDate><atom:link href="https://www.degencode.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[BowTiedDevil]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[degencode@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[degencode@substack.com]]></itunes:email><itunes:name><![CDATA[BowTiedDevil]]></itunes:name></itunes:owner><itunes:author><![CDATA[BowTiedDevil]]></itunes:author><googleplay:owner><![CDATA[degencode@substack.com]]></googleplay:owner><googleplay:email><![CDATA[degencode@substack.com]]></googleplay:email><googleplay:author><![CDATA[BowTiedDevil]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Numerical Optimization — Part II: Derivative-Free Methods]]></title><description><![CDATA[Black Box Optimization]]></description><link>https://www.degencode.com/p/numerical-optimization-part-ii-derivative-free-methods</link><guid isPermaLink="false">https://www.degencode.com/p/numerical-optimization-part-ii-derivative-free-methods</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Fri, 24 Apr 2026 20:00:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Trtf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7581bf0-17a0-4264-8090-9f123b975a2b_800x800.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a href="https://www.degencode.com/p/numerical-optimization-part-i-introduction">Part I</a> outlined the structure of our optimization effort before concluding with a simple benchmark of SciPy&#8217;s <code>minimize_scalar</code> methods.</p><p>To understand how I landed on Brent as the default solver in <a href="https://github.com/BowTiedDevil/degenbot">degenbot</a>, we need to understand the major categories that classify optimization methods.</p><p><a href="https://en.wikipedia.org/wiki/Calculus">Calculus</a> is the branch of mathematics concerning rates of change. The most familiar operation in calculus is the <strong>derivative</strong>, which expresses the rate of change of some function with respect to its input. </p><p>I studied mechanical engineering so I often think of calculus operations with respect to a mapped physical system. </p><p>Let&#8217;s consider a function that maps the displacement of some object from its origin over time:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;x(t) = C \\cdot t&quot;,&quot;id&quot;:&quot;OZZZQVHAHA&quot;}" data-component-name="LatexBlockToDOM"></div><p>This is a simple linear displacement function whereby the object is displaced at a constant rate.</p><p>The velocity of the object over time is modeled by the first derivative of the original function:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;v(t) = \\frac{d}{dt} \\left( x(t) \\right)\n&quot;,&quot;id&quot;:&quot;IGAQVGXCPM&quot;}" data-component-name="LatexBlockToDOM"></div><p>The d/dt notation tells us that the derivative of the enclosed function <strong>f</strong> is taken with respect to the variable <strong>t</strong>. The &#8220;with respect to&#8221; clause is somewhat redundant for a function with only one variable. The distinction becomes important when evaluating systems with multiple variables.</p><p>The mechanics of taking derivatives are not relevant here, but the expression above resolves to:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;v(t) = C&quot;,&quot;id&quot;:&quot;FHZXOBQAUX&quot;}" data-component-name="LatexBlockToDOM"></div><p>Or more plainly, the velocity of the object is a constant value.</p><p>The second derivative can expressed over the original function or its first derivative:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\frac{d}{dt}(v(t)) = \\frac{d}{dt}\\left( \\frac{d}{dt} \\left( x(t) \\right) \\right)&quot;,&quot;id&quot;:&quot;ZPLQLHJJPQ&quot;}" data-component-name="LatexBlockToDOM"></div><p>The second derivative of displacement represents acceleration. We know that an object at constant velocity is not accelerating, so that value is zero.</p><p>There are names for higher-order derivatives, but the first and second are usually all that you need to work through most problems.</p><p>These concepts will appear later when we study Newtonian methods.</p><h2>Fermat&#8217;s Theorem</h2><p>The study of the first derivative allows us to determine some of the system&#8217;s behavior. For systems that exhibit variable behavior, we can identify certain interesting points where the change becomes zero. This may indicate that the system has ceased to change (an object falls from a height and lands without bouncing), or that it has momentarily changed direction (that same object falls from a height, then bounces in the reverse direction).</p><p>In either case, the first derivative becomes zero are one or more points called local extrema. These indicates that the function has been maximized or minimized within some range. These local extrema may also represent global extrema, but not necessarily.</p><h2>Black Box Functions</h2><p>The example above is intentionally simplistic. For functions like these, we can solve for the minimum or maximum directly by taking the first derivative and solving that equation for all inputs that result in zero. Then we can inspect the original function at those values and easily identify the global extrema.</p><p>But life is not always easy, and we often want to optimize a system that can only be observed from the outside. These systems are &#8220;black boxes&#8221; because we have visibility on the inputs and outputs, but incomplete visibility on how they work. We can measure how the system changes empirically and approximate the first derivative, but the absence of a formal output function for that first derivative makes the direct solution infeasible.</p><h2>Derivative-Free Methods</h2><p>Several methods have been developed to handle the task of optimizing black box functions where the derivative cannot be calculated, or where that calculation is unacceptably costly.</p><p>These are commonly known as derivative-free algorithms, and they operate iteratively &#8212; varying the output and studying the change in output until converging on an optimum within some tolerance.</p><p>These methods are generally well understood and documented, which implies they are likely part of the AI training data used to build popular models. </p><p>Using Kimi K2.5, I described the test setup and asked it to summarize derivative-free methods available in SciPy and how they could be used to solve the problem. </p><p>This is the result:</p><blockquote><h3>Nelder-Mead Simplex Method</h3><p>Nelder-Mead maintains a geometric simplex of n+1 vertices (a triangle in 2D, tetrahedron in 3D) where each vertex represents a candidate solution vector of trade amounts across pools. At each iteration, it evaluates the profit at all vertices by delegating to pool classes, identifies the worst-performing vertex, and attempts to improve it by reflecting it through the centroid of the remaining vertices. Depending on how the reflected point compares to the current best and second-worst values, the method may expand (take a larger step), contract (take a smaller step toward the centroid), or shrink the entire simplex toward the best vertex. This approach requires only objective comparisons&#8212;no gradients or smoothness assumptions&#8212;making it robust to the piecewise-constant regions and sudden jumps created by trading fees and liquidity limits in AMM pools.</p><h3>Powell&#8217;s Method (Conjugate Direction Method)</h3><p>Powell&#8217;s method performs sequential line minimizations along a set of directions that are iteratively updated to become conjugate (non-interfering) based on the observed improvement patterns. Starting with coordinate axes as initial search directions, it minimizes the profit function along each direction in turn by finding the optimal step size, then constructs a new conjugate direction from the overall displacement achieved during the cycle. The method replaces the direction that contributed least to the total improvement with this new conjugate direction, gradually learning the local curvature of the objective without ever computing derivatives. While efficient for smooth, locally quadratic objectives, Powell&#8217;s method can stall when encountering flat regions or discontinuities common in arbitrage problems where profits remain constant until crossing a fee threshold or liquidity boundary.</p><h3>Differential Evolution (DE)</h3><p>Differential Evolution is a population-based evolutionary algorithm that maintains a fixed-size swarm of candidate solution vectors, each representing a complete set of trade amounts across pools. For each generation, every individual in the population creates a mutant vector by adding the weighted difference of two randomly selected population members to a third base individual, effectively exploring directions suggested by the population&#8217;s diversity. The mutant then undergoes crossover with the original individual to create a trial vector, which is evaluated against the current individual by delegating both to pool classes for profit calculation; the better performer survives to the next generation. This stochastic, parallel search naturally explores multimodal landscapes with multiple local profit peaks (arising from different pool path combinations), handles discontinuities without issue, and can be embarrassingly parallelized across available compute resources.</p><h3>Covariance Matrix Adaptation Evolution Strategy (CMA-ES)</h3><p>CMA-ES maintains a multivariate Gaussian distribution over the search space, where the mean represents the current best estimate of optimal trade amounts and the covariance matrix captures the pairwise correlations between amounts across different pools. At each iteration, it samples a population of candidate solutions from this distribution, evaluates their profit by delegating to pool classes, and updates the distribution parameters by re-weighting samples based on their ranked performance (not absolute values). The covariance matrix adaptation learns the local structure of the objective&#8212;such as the inherent coupling between trade amounts in multi-hop paths dictated by constant product formulas&#8212;allowing the algorithm to make elongated steps along valleys and navigate ill-conditioned problems efficiently. While highly sample-efficient and self-adaptive regarding step sizes, CMA-ES assumes local quadratic behavior and may stagnate if the profit landscape contains large truly flat regions where all sampled points yield identical values.</p><h3>Constrained Optimization by Linear Approximation (COBYLA)</h3><p>COBYLA is a trust-region algorithm designed for constrained black-box optimization that maintains both the current iterate and a set of &#8220;trust region&#8221; points used to construct linear approximations of the objective and constraint functions. At each iteration, it solves a linear programming subproblem using these approximations to find a step direction that improves profit while respecting constraints (such as slippage limits, available liquidity, or gas bounds), then evaluates the true objective and constraints at the proposed point by delegating to pool classes. The approximations are updated by replacing the oldest trust region point or adjusting the trust region radius based on how well the linear model predicted the actual function values. Unlike penalty-based methods, COBYLA maintains feasibility throughout the optimization (when started from a feasible point) and handles hard constraints natively without requiring derivative information, making it ideal for arbitrage scenarios where constraints like maximum slippage are non-negotiable safety limits.</p><h3>Bayesian Optimization (Gaussian Process-Based)</h3><p>Bayesian optimization builds a probabilistic surrogate model&#8212;typically a Gaussian Process&#8212;over the profit function, treating each evaluation of the black-box pool delegation as an observation that updates the posterior belief about the objective landscape. The Gaussian Process provides both a mean prediction of profit and an uncertainty estimate at any untested point, enabling the algorithm to use an acquisition function (such as Expected Improvement or Upper Confidence Bound) to intelligently select the next trade amount vector that optimally balances exploitation (high predicted profit) against exploration (high uncertainty). After each pool evaluation, the surrogate model is refit and the acquisition function optimized to suggest the next candidate, creating a highly sample-efficient search that minimizes expensive calls to slow or rate-limited pool simulations. While exceptionally efficient when evaluations are costly, the computational overhead of fitting Gaussian Processes scales cubically with the number of observations, making this approach suitable for offline path planning or high-stakes arbitrage validation rather than millisecond-latency production trading.</p><h3>Hooke-Jeeves Pattern Search</h3><p>Hooke-Jeeves performs a two-phase iteration consisting of exploratory moves and pattern moves: the exploratory phase samples points along each coordinate direction from the current base point by taking fixed-size steps, accepting any move that improves profit (even marginally), and uses the resulting point as the new base if any improvement was found. The pattern move then attempts to exploit the discovered trend by jumping from the original base point in the direction of the net displacement achieved during exploration, essentially accelerating along promising directions. If pattern moves continue to succeed, the step size is maintained or increased; if exploration fails to find improvement at a given resolution, the step size is reduced for finer local search. This deterministic, direction-based approach requires no derivatives and minimal bookkeeping, but its reliance on fixed exploratory step sizes and coordinate-aligned searches can lead to slow convergence on rotated or highly correlated objective landscapes like those arising from coupled pool interactions.</p><h3>Basin-Hopping with Local Refinement</h3><p>Basin-hopping is a two-stage global optimization strategy that combines random perturbations with local optimization to escape local profit maxima and find the global optimum across disconnected feasible regions. The algorithm starts from an initial trade amount vector and applies a local optimizer (such as Nelder-Mead or Powell&#8217;s method) to find the nearest local maximum, then randomly perturbs this solution by adding noise scaled to the problem dimensionality and accepts or rejects the new starting point based on a Metropolis criterion or strict improvement requirement. This process iterates, effectively allowing the solver to &#8220;hop&#8221; between basins of attraction in the profit landscape, which is essential for arbitrage problems where different pool paths create multiple distinct local optima. The method is particularly effective when combined with aggressive local optimizers that quickly refine each basin to its peak, trading off the number of expensive pool evaluations per hop against the probability of discovering the globally optimal arbitrage path.</p><h3>Simulated / Dual Annealing</h3><p>Simulated annealing is a probabilistic meta-heuristic inspired by the metallurgical annealing process, where the algorithm starts with high &#8220;temperature&#8221; allowing large random steps and gradually cools, reducing both step sizes and the probability of accepting worsening moves according to a Boltzmann-like acceptance criterion. At high temperatures, the solver explores broadly across the trade amount space, delegating to pools and accepting occasional profit decreases to escape local optima; as temperature decreases, it increasingly rejects inferior solutions and converges toward the global maximum. Dual annealing extends this with an adaptive strategy that combines classical annealing with local search visits and uses a fast annealing schedule for global exploration. While theoretically guaranteed to find the global optimum given infinite time and logarithmic cooling, simulated annealing typically requires many more objective evaluations than population-based methods to achieve comparable convergence, making it less suitable for time-sensitive arbitrage scenarios unless profit evaluations are extremely cheap.</p></blockquote><h2>Just Put My Benchmark in the Bag Bro</h2><p>That&#8217;s nice and all, but what we really care about it speed and accuracy. So let&#8217;s ask the model to implement and evaluate them:</p>
      <p>
          <a href="https://www.degencode.com/p/numerical-optimization-part-ii-derivative-free-methods">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Numerical Optimization — Part I: Introduction]]></title><description><![CDATA[Who Optimizes the Optimizers?]]></description><link>https://www.degencode.com/p/numerical-optimization-part-i-introduction</link><guid isPermaLink="false">https://www.degencode.com/p/numerical-optimization-part-i-introduction</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Tue, 21 Apr 2026 18:32:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G6XT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>MEV searching boils down to a set of critical elements: pathfinding, numerical optimization, execution.</p><p>Numerical optimization is a broad category concerning the question &#8220;what is the optimal input that satisfies the constraints of my problem?&#8221;</p><p>This question is highly relevant to us. After identifying a price mismatch across some asset market, we must determine the most profitable route to capture value and how to size the associated trades.</p><p>I first covered numerical optimization with a lesson covering the Brent solver implemented in <a href="https://scipy.org/">SciPy</a>.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;f3c30410-d8fd-49c3-b1de-659f91c4d04e&quot;,&quot;caption&quot;:&quot;The trickiest part of a two-pool arbitrage is determining the optimal borrow amount.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Smart Contract Arbitrage &#8212; Optimizing the Input&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2022-03-16T04:52:36.958Z&quot;,&quot;cover_image&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/smart-contract-arbitrage-optimizing&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:50429039,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:23,&quot;comment_count&quot;:4,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Later I focused on convex optimization:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;a82615b2-4962-4b2d-980f-a5d86e62fe96&quot;,&quot;caption&quot;:&quot;If you spend enough time on Twitter and follow smart people, you&#8217;ll see &#8220;convex optimization&#8221; mentioned.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Convex Optimization &#8212; Introduction&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2024-07-24T23:12:27.707Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cc59c1a-da34-40ab-939d-ac79082a5e0b_860x809.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/convex-optimization-part-i-introduction&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:146861328,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:4,&quot;comment_count&quot;:0,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>Playing To Your Strengths</h2><p>It&#8217;s important to understand your strengths and weaknesses, and even more important to choose efforts that are helped by your strengths, not harmed by your weaknesses, or ideally both.</p><p>I&#8217;m above average at abstract thinking, verbal reasoning, working memory, and synthesis (applying techniques from multiple domains). But I&#8217;m below average at mental processing speed, spatial reasoning, and numeric intuition. </p><p>This sets me up for trouble when I&#8217;m faced with certain classes of problems, and it compounds if that problem has multiple non-obvious solutions.</p><p>When I encounter some numerical optimization problem, I often have little intuition about how to attack it, falling back on brute-force methods and taking the first solution that works without spending much time on it.</p><p>Luckily, I now have access to highly capable AI tools that can fill in these gaps. </p><p>I already showed how to use AI to boost feature development and debugging.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;ab3fe280-8897-4693-8a2b-1cb9f352f3ad&quot;,&quot;caption&quot;:&quot;I have been using AI to chew through some significant Aave V3 complexity recently.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Agentic Coding For Degens&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-02-10T23:05:46.363Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!vlNY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/agentic-coding&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:187546090,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:13,&quot;comment_count&quot;:2,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Then I showed how to use it to quickly iterate on developing smart contracts.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;d9c47046-08c0-4b66-abde-00cd3d3ca5fb&quot;,&quot;caption&quot;:&quot;With user data ready, we can turn our concentration to executing the liquidations.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Aave &#8212; Part IX: Execution Contract&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-04-10T20:59:11.280Z&quot;,&quot;cover_image&quot;:null,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/aave-part-ix-execution-contract&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:193502349,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:3,&quot;comment_count&quot;:0,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Now I&#8217;m going to demonstrate how I&#8217;m using it to refine parts of my code base with methods that would be tedious to apply by hand.</p><p>This will be a multi-part series because the scope is too broad for a single entry. Even after I reach a reasonable stopping point, I may continue adding new entries as I discover and implement new techniques.</p><h2>Problem &amp; Test Setup</h2><p>We need a way to compare methods across runs, so we need to build a reproducible set of inputs that model a relevant problem in our domain. </p><p>Since the majority of our work concerns liquidity pools from Uniswap and its various forks, we need a way to generate synthetic pool states associated with a given arbitrage scenario. I gave this to an agent, which ultimately proposed this architecture:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;aecbf77b-8358-4aca-9275-7497cb8afc77&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">   &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
   &#9474;                         FixtureFactory                              &#9474;
   &#9474;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;  &#9474;
   &#9474;  &#9474;                    PoolStateGenerator                         &#9474;  &#9474;
   &#9474;  &#9474;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;&#9474;  &#9474;
   &#9474;  &#9474;  &#9474;  V2 Pools   &#9474;  &#9474;  V3 Pools   &#9474;  &#9474;  V4 Pools               &#9474;&#9474;  &#9474;
   &#9474;  &#9474;  &#9474;  (reserves) &#9474;  &#9474;(tick-based) &#9474;  &#9474;(pool ID + tick-based)   &#9474;&#9474;  &#9474;
   &#9474;  &#9474;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;&#9474;  &#9474;
   &#9474;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;  &#9474;
   &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                                 &#9474;
                                 &#9660;
                      &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
                      &#9474; ArbitrageCycleFixture&#9474;
                      &#9474; (frozen dataclass)   &#9474;
                      &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</code></pre></div><p>I&#8217;m omitting considerable detail on how this works under the hood, but the takeaway is that we can use this setup to mix and match arbitrage scenarios across the different pool types. </p><h3>Scenario: Basic Two-Pool</h3><p>Generate a basic scenario using two Uniswap V2 pools with a fixed price ratio and a target liquidity:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;bc82d4fb-b888-4b38-b882-25515a6161d7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def simple_v2_arb_profitable(self) -&gt; ArbitrageCycleFixture:
    """
    Two V2 pools with 2% price difference.

    Simple profit: arbitrage from higher to lower price pool.
    Pool A: 1 ETH = 2000 USDC (price = 2000)
    Pool B: 1 ETH = 1960 USDC (price = 1960, 2% lower)

    Arbitrage: Buy ETH in pool B (cheaper), sell in pool A (more expensive).
    """
    pool_a_address: ChecksumAddress = cast(
        "ChecksumAddress", "0x0000000000000000000000000000000000000001"
    )
    pool_b_address: ChecksumAddress = cast(
        "ChecksumAddress", "0x0000000000000000000000000000000000000002"
    )
    input_token_address: ChecksumAddress = cast(
        "ChecksumAddress", "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
    )  # USDC

    # Generate pools with 2% price difference
    pool_a, pool_b = self._generator.generate_profitable_v2_pair(
        pool_a_address=pool_a_address,
        pool_b_address=pool_b_address,
        fee_a=Fraction(3, 1000),
        fee_b=Fraction(3, 1000),
        price_ratio=1.02,
        liquidity_base=10**21,  # ~1000 ETH equivalent
    )

    return ArbitrageCycleFixture(
        id="simple_v2_arb_profitable",
        cycle_type="v2_v2",
        pool_states={pool_a_address: pool_a, pool_b_address: pool_b},
        input_token_address=input_token_address,
        expected_optimal_input=0,  # Calculated by solver
        expected_profit=0,  # Calculated by solver
    )</code></pre></div><h2>SciPy Benchmark</h2><p>Now let&#8217;s compare the three methods implemented by SciPy&#8217;s <a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html">minimize_scalar</a> solver (Bounded, Brent, and Golden):</p>
      <p>
          <a href="https://www.degencode.com/p/numerical-optimization-part-i-introduction">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Aave — Part IX: Execution Contract]]></title><description><![CDATA[Getting AI To Do The Heavy Lifting]]></description><link>https://www.degencode.com/p/aave-part-ix-execution-contract</link><guid isPermaLink="false">https://www.degencode.com/p/aave-part-ix-execution-contract</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Fri, 10 Apr 2026 20:59:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G6XT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>With user data ready, we can turn our concentration to executing the liquidations.</p><p>I have built many execution contracts before for various projects (search the <a href="https://www.degencode.com/archive">archives</a> if you&#8217;d like a refresher on them).</p><p>They all have a similar structure:</p><ul><li><p>Validate that the caller is authorized, then process their inputs</p></li><li><p>Record the starting balance</p></li><li><p>Perform a sequence of validated actions across a set of smart contracts</p></li><li><p>Record the final balance, reverting if it has decreased</p></li></ul><p>This is a well-understood pattern with plenty of examples, which is an ideal setup for agentic AI. My last entry on agentic coding was well-received, so I expect this will be useful too.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;419a89a6-909b-4152-aea1-8dbb4805a806&quot;,&quot;caption&quot;:&quot;I have been using AI to chew through some significant Aave V3 complexity recently.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Agentic Coding For Degens&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2026-02-10T23:05:46.363Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!vlNY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/agentic-coding&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:187546090,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:12,&quot;comment_count&quot;:2,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>Agent Setup</h2><p>For this effort, I created a new project in directory <code>~/code/aave_v3_liquidator</code> and created a minimal Python 3.12 virtual environment using <code>uv</code> with these packages:</p><ul><li><p><code>eth-ape</code> with the <code>recommended-plugins</code> extra</p></li><li><p><code>degenbot</code> 0.6.0a2</p></li><li><p><code>vyper</code> 0.4.3</p></li></ul><p>I used <a href="https://pi.dev/">Pi</a> version 0.66.1, calling <a href="https://huggingface.co/moonshotai/Kimi-K2.5">Kimi K2.5</a> through <a href="https://synthetic.new/?referral=zL07O85jYXTPOcZ">Synthetic</a>.</p><h2>Session Transcript</h2><p>You can review the <a href="https://pi.dev/session/#41f7fcca7c1a200148f24d0ddde6b06c">complete transcript</a> if you&#8217;re curious.</p><p>Let&#8217;s break down the session with some context to help read between the lines:</p><h2>The Task</h2><p>I started the session with a task definition:</p><blockquote><p>Help me prepare a plan for this project: create a Vyper smart contract that can execute an atomic Aave V3 liquidation against a given user. It will use the Uniswap V4 PoolManager as its "bank" and "market", i.e. it should perform an <code>unlock</code>, then within the V4 callback, execute a <code>liquidationCall</code> at the Aave V3 Pool and a <code>swap</code> at the V4 PoolManager to handle these operations: a withdrawal of the debt token (sent to the Aave V3 Pool), a deposit of the collateral token (received from the Aave liquidation), and optionally another swap to consolidate profit in a given token. There are some subtleties regarding execution order that we will have to study, but I can retrieve contract source code as needed.</p></blockquote><p>Then it thought for a bit, proposed a design, and gave a summary:</p><blockquote><p><strong>Next steps:</strong></p><ul><li><p>I can retrieve the Aave V3 Pool source code to verify <code>liquidationCall</code> return values</p></li><li><p>I can retrieve Uniswap V4 PoolManager interface to confirm <code>unlockCallback</code> signature</p></li><li><p>Do you have a specific network in mind (Ethereum mainnet, testnet)?</p></li></ul><p>Would you like me to start implementing the contract, or should we fetch and analyze the dependency contracts first to nail down the exact interfaces?</p></blockquote><p>So I gave it some more context:</p><blockquote><p>We are working on Ethereum mainnet. Retrieve the Aave V3 Pool source code by using <code>cast</code>. First retrieve the implementation address from the proxy contract at <strong>0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2</strong>, then get its source. The Uniswap V4 PoolManager contract is not a proxy, so retrieve its source directly at <strong>0x000000000004444c5dc75cB358380D2e3dE08A90</strong></p></blockquote><p>It read the <code>cast</code> skill to learn how to use Foundry correctly, then downloaded the contract source code for the Aave V3 Pool implementation contract at <a href="https://etherscan.io/address/0x8147b99df7672a21809c9093e6f6ce1a60f119bd">0x8147b99df7672a21809c9093e6f6ce1a60f119bd</a> and the Uniswap V4 PoolManager. It read through them to discover the execution flow for the key functions, and then summarized the operations:</p><blockquote><p>I&#8217;ve retrieved both contracts. Here are the key findings:</p><p><strong>Summary of Retrieved Contracts</strong></p><p></p><p><strong>Aave V3 Pool (Proxy: </strong><code>0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2</code><strong>)</strong></p><ul><li><p><strong>Implementation</strong>: <code>0x8147b99df7672a21809c9093e6f6ce1a60f119bd</code></p></li><li><p><strong>Key Function</strong>:</p></li></ul><pre><code><code>function liquidationCall(
    address collateralAsset,
    address debtAsset,
    address borrower,
    uint256 debtToCover,
    bool receiveAToken  // false = receive underlying collateral
) external;</code></code></pre><ul><li><p><strong>No return value</strong> - collateral is transferred directly to <code>msg.sender</code></p></li></ul><p></p><p><strong>Uniswap V4 PoolManager (</strong><code>0x000000000004444c5dc75cB358380D2e3dE08A90</code><strong>)</strong></p><ul><li><p><strong>Key Interface</strong>:</p></li></ul><pre><code><code>function unlock(bytes calldata data) external returns (bytes memory);

// Callback that MUST be implemented
interface IUnlockCallback {
    function unlockCallback(bytes calldata data) external returns (bytes memory);
}

function take(Currency currency, address to, uint256 amount) external;
function settle() external payable returns (uint256 paid);
function settleFor(address recipient) external payable returns (uint256 paid);

function swap(PoolKey memory key, SwapParams memory params, bytes calldata hookData)
    external returns (BalanceDelta swapDelta);

struct SwapParams {
    bool zeroForOne;
    int256 amountSpecified;  // negative = exact input, positive = exact output
    uint160 sqrtPriceLimitX96;
}</code></code></pre><p></p><p><strong>Updated Execution Plan</strong></p><p>Based on the source code, here&#8217;s the refined execution flow:</p><pre><code><code>&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474;  unlock(data)    &#9474;
&#9474;   (caller = us)  &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
         &#8595;
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;     take(debtAsset, amount)       &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474; unlockCallback() &#9474;  &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#8594; &#9474;  PoolManager&#9474;
&#9474;   (us via PM)    &#9474;                                   &#9474;             &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;                                   &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
         &#8595;
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474;approve(debtAsset)&#9474;  &#8594;  liquidationCall(...)  &#8594;  receive collateral
&#9474;  (to Aave Pool)  &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
         &#8595;
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;     Optional: swap collateral&#8594;profit
&#9474;  Optional swap   &#9474;  &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#8594;
&#9474; (if consolidate) &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
         &#8595;
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;     swap/debt operations to acquire debtAsset
&#9474;  Acquire debt    &#9474;  &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#8594;
&#9474;  for repayment   &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
         &#8595;
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;     settle() - repay flash borrow
&#9474;     settle()     &#9474;  &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#8594;
&#9474;  (debtAsset)     &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
         &#8595;
    Validation: All deltas must be zero
         &#8595;
    Return profit to caller</code></code></pre><p></p><p><strong>Key Implementation Details</strong></p><p><strong>1. Execution Order (Critical)</strong></p><pre><code><code># Inside unlockCallback:
1. take(debtAsset, self, debt_amount)    # Create liability
2. approve(debtAsset, aave_pool, debt_amount)
3. liquidationCall(...)                   # Receive collateral
4. [Optional] swap(collateral&#8594;profit)    # Consolidate
5. swap(profit&#8594;debtAsset) or take        # Acquire repayment funds
6. settle(debtAsset)                      # Clear liability</code></code></pre><p><strong>2. Vyper Interface Definitions</strong></p><pre><code><code># Aave V3 Pool
interface IPool:
    def liquidationCall(
        collateralAsset: address,
        debtAsset: address,
        borrower: address,
        debtToCover: uint256,
        receiveAToken: bool
    ): nonpayable

# Uniswap V4 PoolManager
interface IPoolManager:
    def unlock(data: Bytes[1024]) -&gt; Bytes[1024]: nonpayable
    def take(currency: address, to: address, amount: uint256): nonpayable
    def settle() -&gt; uint256: payable
    def swap(
        key: PoolKey,
        params: SwapParams,
        hookData: Bytes[256]
    ) -&gt; BalanceDelta: nonpayable

# Must implement
interface IUnlockCallback:
    def unlockCallback(data: Bytes[1024]) -&gt; Bytes[1024]: nonpayable</code></code></pre><p><strong>3. Critical Subtleties</strong></p><ul><li><p><strong>Callback validation: </strong>Check <code>msg.sender == pool_manager</code> inside <code>unlockCallback</code></p></li><li><p><strong>PoolKey ordering: </strong>V4 requires <code>currency0 &lt; currency1</code>; need sorting helper</p></li><li><p><strong>BalanceDelta: </strong>Track swap returns - negative means received, positive means owed</p></li><li><p><strong>Token approvals: </strong>Infinite approve Aave Pool and PoolManager in constructor</p></li><li><p><strong>Native ETH: </strong>Use WETH for ETH positions; PoolManager handles via Currency type</p></li></ul><p><strong>4. Profit Flow</strong></p><ol><li><p>Debt tokens are <code>taken</code> from PoolManager (creates liability)</p></li><li><p>Liquidation repays debt, receives collateral (+ liquidation bonus)</p></li><li><p>Optional: Swap collateral for preferred profit token</p></li><li><p>Acquire debt tokens to <code>settle</code> the liability</p></li><li><p>Remaining tokens = profit &#8594; transfer to caller</p></li></ol><p>Would you like me to proceed with implementing the Vyper contract now, or do you need any clarification on the execution flow?</p></blockquote><p>Most models are trained to be helpful and maintain velocity, so you&#8217;ll notice that it &#8220;wants&#8221; to guide us down the route of progress: either begin implementing the contract immediately, or offer explanation of its proposed design.</p><p>Here is the point where things could go off the rails. There are a lot of assumptions baked into the proposed design, and I like to bring them to the surface.</p><p>So I choose a third route:</p>
      <p>
          <a href="https://www.degencode.com/p/aave-part-ix-execution-contract">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Aave — Part VIII]]></title><description><![CDATA[Price Oracles]]></description><link>https://www.degencode.com/p/aave-part-viii-price-oracles</link><guid isPermaLink="false">https://www.degencode.com/p/aave-part-viii-price-oracles</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Fri, 03 Apr 2026 18:33:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!0deg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fbc8b82-8e1b-4d93-8906-b81ca5b574e5_1616x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We now have access to the complete set of Aave user positions, so we can analyze them and begin hunting. </p><p>To properly evaluate the health of a particular user, we need to know all of their collateral and debt positions. In <a href="https://www.degencode.com/p/aave-part-v-liquidation-valuation-user-asset-discovery">Part V</a>, we did this by hand using mentally mapped asset prices.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;a198a6ca-e72d-4e93-9acb-b80bea05d37a&quot;,&quot;caption&quot;:&quot;We discovered in Part IV that liquidatable positions are frequently present on Aave. But it&#8217;s not enough to find all the positions below the liquidation threshold and clear them out &#8212; position size and gas fees factor into the overall profit.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Aave &#8212; Part V&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-12-18T00:10:55.849Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!dPPk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c245f6-f959-49b3-a48d-d60ca0565576_1962x1104.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/aave-part-v-liquidation-valuation-user-asset-discovery&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:181452218,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Now we want to replace that process (with its assumptions and simplifications) with one that scales across all of Aave&#8217;s assets. To do this, we need to look closely at how asset prices are determined so that we can track them in the same way.</p><h2>Price Oracles</h2><p>I&#8217;ve discussed price oracles before in a limited way, focusing on the price of a single asset.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;d2d14fc3-c8d5-461a-bf95-356c7085da1f&quot;,&quot;caption&quot;:&quot;So far I&#8217;ve concerned myself with two types of token pools:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Chainlink Price Feeds, Calculating Trade EV, Automatically Scaling Gas Fees&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2022-01-17T23:38:18.360Z&quot;,&quot;cover_image&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/chainlink-price-feeds-calculating&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:47230301,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:28,&quot;comment_count&quot;:8,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Aave uses an <a href="https://etherscan.io/address/0x54586be62e3c3580375ae3723c145253060ca0c2">oracle contract</a> that acts as a bridge between its internal operations and the external market. The current oracle is available at the <a href="https://etherscan.io/address/0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e#readContract">PoolAddressProvider</a> contract via the <code>getPriceOracle</code> function. The oracle was set shortly after the mainnet market deployment and has not changed since, but that is not a guarantee that it will remain that way. It can be changed at any time by governance.</p><h2>Asset Sources</h2><p>The oracle maintains a single source for each of the registered assets. Whenever this source is updated, the oracle emits an <code>AssetSourceUpdated</code> event. The Aave updater in <code>degenbot</code> tracks these events and uses them to update the database.</p><h2>Asset Prices</h2><p>The oracle&#8217;s price discovery mechanism is the <code>getAssetPrice</code> function, which retrieves the source for a given asset, then calls <code>source.latestAnswer()</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;c&quot;,&quot;nodeId&quot;:&quot;9c386607-b3be-48d9-9e17-c4805340836b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-c">/// @inheritdoc IPriceOracleGetter
function getAssetPrice(address asset) public view override returns (uint256) {
  AggregatorInterface source = assetsSources[asset];

  if (asset == BASE_CURRENCY) {
    return BASE_CURRENCY_UNIT;
  } else if (address(source) == address(0)) {
    return _fallbackOracle.getAssetPrice(asset);
  } else {
    int256 price = source.latestAnswer();
    if (price &gt; 0) {
      return uint256(price);
    } else {
      return _fallbackOracle.getAssetPrice(asset);
    }
  }
}</code></pre></div><p><code>latestAnswer()</code> should be familiar if you&#8217;ve looked at a Chainlink contract. Note that the oracle does not have to be a Chainlink contract, but most of them are. The only requirement of the oracle is that is provides the <code>latestAnswer</code> interface. </p><p>So all we need is the source for each asset, and a way to observe the price when it changes.</p><p>Let&#8217;s consider Aave&#8217;s Ether/WETH asset. The oracle tells us that the price source is contract <a href="https://etherscan.io/address/0x5424384B256154046E9667dDFaaa5e550145215e">0x5424384B256154046E9667dDFaaa5e550145215e</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!R_Rb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!R_Rb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png 424w, https://substackcdn.com/image/fetch/$s_!R_Rb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png 848w, https://substackcdn.com/image/fetch/$s_!R_Rb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png 1272w, https://substackcdn.com/image/fetch/$s_!R_Rb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!R_Rb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png" width="1456" height="547" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:547,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:71078,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/192900860?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!R_Rb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png 424w, https://substackcdn.com/image/fetch/$s_!R_Rb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png 848w, https://substackcdn.com/image/fetch/$s_!R_Rb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png 1272w, https://substackcdn.com/image/fetch/$s_!R_Rb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d5b2b9a-d9d9-4fee-90e9-f555f8cf809c_1614x606.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And that the asset&#8217;s current price is 205429135979 ($2,054):</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9dMX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9dMX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png 424w, https://substackcdn.com/image/fetch/$s_!9dMX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png 848w, https://substackcdn.com/image/fetch/$s_!9dMX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png 1272w, https://substackcdn.com/image/fetch/$s_!9dMX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9dMX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png" width="1456" height="548" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:548,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:63779,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/192900860?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9dMX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png 424w, https://substackcdn.com/image/fetch/$s_!9dMX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png 848w, https://substackcdn.com/image/fetch/$s_!9dMX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png 1272w, https://substackcdn.com/image/fetch/$s_!9dMX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11d7cc36-3d8f-4b39-b2a2-30ba7d390903_1616x608.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We can also inspect <code>latestAnswer()</code> directly at the source contract:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!r2XN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!r2XN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png 424w, https://substackcdn.com/image/fetch/$s_!r2XN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png 848w, https://substackcdn.com/image/fetch/$s_!r2XN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png 1272w, https://substackcdn.com/image/fetch/$s_!r2XN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!r2XN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png" width="1456" height="283" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:283,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:65986,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/192900860?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!r2XN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png 424w, https://substackcdn.com/image/fetch/$s_!r2XN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png 848w, https://substackcdn.com/image/fetch/$s_!r2XN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png 1272w, https://substackcdn.com/image/fetch/$s_!r2XN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af7f044-c5da-4b88-a7f2-1f06518736ff_1606x312.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The values match, which we expected.</p><h2>Chainlink SVR</h2><p>It&#8217;s not obvious from reading the contract source, but this is a special kind of feed contract labeled as a Chainlink Smart Value Recapture (SVR) feed also specific to the Aave protocol.</p>
      <p>
          <a href="https://www.degencode.com/p/aave-part-viii-price-oracles">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Degenbot v0.6.0 (alpha 1) Release]]></title><description><![CDATA[Aave V3 Testers Wanted]]></description><link>https://www.degencode.com/p/degenbot-v060-alpha1-release-</link><guid isPermaLink="false">https://www.degencode.com/p/degenbot-v060-alpha1-release-</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Mon, 30 Mar 2026 23:14:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G6XT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a href="https://aave.com/blog/aave-v4-live-ethereum">Aave&#8217;s V4 market</a> went live on Mainnet this morning, congratulations to them! </p><p>Coincidentally I&#8217;ve published a <a href="https://pypi.org/project/degenbot/0.6.0a1.post1/">new degenbot release</a> with Aave V3 position scraping enabled. If you&#8217;re interested in testing it, you can install it using <code>pip install degenbot==0.6.0a1.post1. </code></p><p>You can populate your database with positions using these commands:</p><ul><li><p>Upgrade the database using <code>degenbot database upgrade</code></p></li><li><p>Activate the market using <code>degenbot aave activate ethereum_aave_v3 </code></p></li><li><p>Scrape the positions using <code>degenbot aave update</code></p></li></ul><p>In addition to Aave V3, this release is notable because it&#8217;s the first one distributed with integrated Rust components via <a href="https://pyo3.rs/">PyO3</a>. </p><p>I will be integrating more Rust into the package, so please report any bugs that you encounter on these 0.6 releases. </p><p>The installation should work cleanly on Linux (x86_64, aarch64), MacOS (x86_64 and ARM64), and Windows (x86_64). If you have exotic hardware and want to help me test the package against those, please let me know!</p>]]></content:encoded></item><item><title><![CDATA[Aave — Part VII]]></title><description><![CDATA[Unraveling Complexity]]></description><link>https://www.degencode.com/p/aave-part-vii-unraveling-complexity</link><guid isPermaLink="false">https://www.degencode.com/p/aave-part-vii-unraveling-complexity</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Tue, 24 Mar 2026 19:46:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G6XT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a href="https://www.degencode.com/p/aave-part-vi-adding-db-positions">Part VI</a> closed with a simple demonstration of building collateral positions using a simple process: look for Supply events, then decode them to inspect the user address, the reserve address, and the amount supplied.</p><p>But that&#8217;s a naive approach that ignores a lot of detail. Critically, it scales poorly &#8212; every successive update would have to scan for new events since the last block, discover new users, add them to the previous users, then do a full re-scrape of all of the previous plus new positions.</p><p>So instead of rely on that structure, I built the updater from the ground up. Along the way, I dug through many layers of unexpected complexity. It required a lot of untangling, but yielded valuable information about how Aave handles position accounting, upgrades, and changing incentives.</p><p>I will cover several areas where position tracking required additional layers of decoding and tracking. This will be particularly useful if you need to understand how the Aave contracts are wired together across a particular market.</p>
      <p>
          <a href="https://www.degencode.com/p/aave-part-vii-unraveling-complexity">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Agentic Coding For Degens]]></title><description><![CDATA[How To Automate The Boring Stuff]]></description><link>https://www.degencode.com/p/agentic-coding</link><guid isPermaLink="false">https://www.degencode.com/p/agentic-coding</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Tue, 10 Feb 2026 23:05:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vlNY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have been using AI to chew through some significant Aave V3 complexity recently. </p><p>I&#8217;ve learned a lot, and will certainly refer to these techniques again. So instead of folding it into an Aave specific write-up where it could be missed, I&#8217;m giving it proper treatment with its own introduction.</p><h2>AI Fatigue</h2><p>Every week I&#8217;m being told that it&#8217;s all over: a script will take my job, the robots are coming to kill me, RAM will never be affordable again, I should shut up and get in the pod.</p><p>It&#8217;s neither as bad nor as good as they say. Relentless media treatment has made &#8220;AI&#8221; a container word &#8212; people hear it, fill in ideas and things they&#8217;ve heard about it, and then stop thinking.</p><p>You can respond to AI any way you like. Here are my very important opinions on it:</p><ul><li><p>Seeing fake AI avatars everywhere is annoying, but I like generating coloring pages for my kids and diagrams for technical presentations.</p></li><li><p>AI bots clogging up the replies on X is infuriating, but I like to get a summary of long documents.</p></li><li><p>The chat bot on my car dealership&#8217;s website is stupid, but the one integrated into open-source tool documentation is very useful.</p></li></ul><h2>Coding Tools</h2><p>AI has been around for many decades, and its sophistication and usefulness has been trending upwards. It crossed the usefulness threshold in recent years, and appears to be here to stay. </p><p>I only started paying attention to this a few years ago.</p><h3>Autocomplete</h3><p>I saw folks talking about GitHub Copilot, so tried it out. After a few days I turned it off &#8212; I didn&#8217;t find the auto-completion particularly useful, and was frequently distracted by the phantom text appearing when I stopped typing.</p><h3>Web Chat</h3><p>Then Claude launched with the familiar web interface. I saw more people talking about it, saying that Opus 4 was a good coding model, so I signed up for a few months. </p><p>I found it was very good at generating small scale code snippets that I could copy and paste into my project. But the experience fell apart quickly when the code didn&#8217;t work as expected &#8212; I had to dump long tracebacks into the chat and ask it for fixes, then perform another copy-paste cycle. I felt limited by the browser interface and frustrated by how poorly it performed as the session grew in length. I cancelled.</p><h3>Agentic</h3><p>Then I started seeing chatter about agentic coding techniques. The promise was that agents could be spawned which would operate independently towards some goal.</p><p>I was specifically interested in the coding agents. A coding agent is simply a tool that tracks user inputs, calls an AI service, interprets responses, executes tools on behalf of the AI and manages I/O. </p><h4>Editor Based</h4><p>The first coding agents tools I saw were hooked to a graphical editor via a plug-in or natively: Cursor, Windsurf, Cline, Zed, Roo. </p><p>I tried them and enjoyed the ability to easily focus the tool on a section of code or an error message in the terminal. I continued to get nice results on small-scale tasks, but large refactoring efforts felt quite clumsy and hard to track if they needed multiple phases. I had the feeling I had traded writing code (which I enjoy very much) for micromanaging a capable but easily distracted person (which I dislike very much).</p><h4>Command Line Based</h4><p>Then Claude Code dropped in the middle of last year and everything changed!</p><p>It&#8217;s difficult to separate the improvement in coding tools from the improvement in coding-focused AI models, so I won&#8217;t try. But I will assert that console tools like Claude Code are a huge improvement over both web tools and editor tools. </p><p>The web comparison is easy to demonstrate. Imagine using Claude Opus 4.5 through the web interface. Still very capable, but having to copy and paste is cumbersome and not scalable.</p><p>The IDE tool comparison is harder, but the weakness shared with the web tool is the mode of user interaction. You still enter prompts with a keyboard and approve actions with a mouse. Very compatible with the typical way people work, but still limited by user attention and availability.</p><h2>Code Is Now Cheap</h2><p>In a post-AI world, code has become cheap so you should write less of it! </p><p>Your time is now too expensive to spend writing your own loops. But what should you do instead?</p><h3>You Can Just (Not) Do Things</h3><p>Appearing busy is glamorous: you look important and get the warm feeling that you&#8217;ve maximized your productivity. </p><p>But the result of your efforts can vary wildly depending on the tasks you select.</p><p>For coders, this shows up as a ratio between features and products shipped vs. time spent creating it. </p><p>In terms of processing speed and I/O, humans are the bottleneck in any process involving computers. So learning how to extract yourself from the &#8220;hot&#8221; loop is critical.</p><p>Several approaches have come from this realization.</p><h3>Vibe Coding</h3><p>Andrej Karpathy&#8217;s <a href="https://x.com/karpathy/status/1886192184808149383">famous &#8220;vibe coding&#8221; tweet</a> has pushed many people towards giving the AI more control. Instead of trying to act as the brain and letting the AI be your fingers, vibe coding encourages stepping away and reviewing the output later, then nudging the result to taste.</p><p>The tweet literally says it&#8217;s &#8220;not too bad for throwaway weekend projects&#8221;, so it&#8217;s obviously not a good technique for writing production software.</p><p>Once you see the spaghetti generated from a long vibe coding session, you&#8217;ll realize the problem. AIs are good at generating code &#8212; the more you use it the more you get. </p><p>The usual AI solution to handling complex inputs tends towards branching and special-case fixes. Ask it to consolidate the code using DRY techniques and it will just extract a bunch of helper functions or hide the branching in some new method, without resolving the architectural issue. It can quickly spiral away when not properly constrained.</p><h3>Ralph Wiggum</h3><p>I recommend watching this primer on the Ralph Wiggum loop by its creator, Geoffrey Huntley:</p><div id="youtube2-4Nna09dG_c0" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;4Nna09dG_c0&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/4Nna09dG_c0?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Here is the basic Ralph technique:</p><ul><li><p>Generate a highly detailed plan and specifications for what you want to build</p></li><li><p>Run a coding agent in a loop with these instructions:</p><ul><li><p>Read the plan</p></li><li><p>Read the specifications</p></li><li><p>Select the most important single task from the plan</p></li><li><p>Complete it</p></li><li><p>Run the tests</p></li><li><p>Commit</p></li><li><p>Mark the task as complete</p></li><li><p>Add important lessons learned to a file</p></li><li><p>If all tasks are complete, take some action that terminates the loop</p></li></ul></li></ul><p>In this way, each iteration of the loop starts fresh with a context window focused on important items and a simple request: implement one task. </p><p>Each loop starts the agent with its context reset, so that it can load only what it needs instead of holding on to many irrelevant instructions that degrade accuracy.</p><h2>Automation</h2><p>I&#8217;m a huge fan of using command line interface (CLI) tools. They are flexible, high performance, and portable. But the killer feature is they can be automated!</p><p>This whole newsletter is about building autonomous stuff, so this shouldn&#8217;t surprise you. If I can eliminate a click or button press, or make something run on a schedule, I will. I&#8217;m willing to spend a lot of time converting a manual process to automatic.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vlNY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vlNY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vlNY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vlNY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vlNY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vlNY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg" width="564" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:564,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:84335,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/187546090?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!vlNY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vlNY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vlNY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vlNY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e18f2ff-cb99-49c9-bd2f-f73e81b7bc93_564x500.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>So naturally when I code, I do it with agentic tools on the CLI that call AI models specifically trained for coding.</p><p>I have a strong bias towards open source, so I prefer using an open source agent calling open weight models through a neutral provider. Currently I use <a href="https://opencode.ai/">OpenCode</a> with <a href="https://www.kimi.com/ai-models/kimi-k2-5">Kimi K2.5</a> via <a href="https://synthetic.new/?referral=zL07O85jYXTPOcZ">Synthetic</a> (referral link, we&#8217;ll both get a promotional credit if you sign up).</p><h3>Automatic Plan Summary</h3><p>My first automation was simple: generate a daily summary of the open plans.</p><p>OpenCode allows you to define a custom command via a Markdown file. So I wrote one with instructions on how to do that:</p><pre><code>---
description: Review project plans and provide a summary
agent: plan
model: synthetic/hf:moonshotai/Kimi-K2.5
---

Use the Glob tool to find work plans organized in `.opencode/plans`. Simple plans in the root directory are saved as individual files. Complex plans spanning multiple files are saved in a subdirectory.

For each plan, delegate the following actions:
- Review the progress described in the plan versus the referenced code
- Provide a summary of actual progress made
- Recommend updates to the plan to synchronize it with actual progress
- Recommend improvements to the plan

Then:
- Summarize the state of each plan and action items
- Prioritize the pending work
- Recommend one action item to make progress on each plan</code></pre><p>This command can be executed by calling OpenCode with the appropriate option. Here you can see that it looks for the plans using the Glob tool (an abstraction over <code>find</code>) in the working directory, then delegates review of each plan to a subagent that starts fresh and can concentrate on the one plan it was given: </p><p><code>summarize.md</code></p><pre><code>btd@dev:~/code/degenbot$ opencode run --command summarize

&gt; plan &#183; hf:moonshotai/Kimi-K2.5

&#10033; Glob ".opencode/plans/**/*" 7 matches
&#8594; Read .opencode/plans/hybrid_configuration/implementation_plan.md
&#8594; Read .opencode/plans/aave_transaction_refactor.md
&#8594; Read .opencode/plans/uniswap_v3_pool_refactoring/plan.md
&#8594; Read .opencode/plans/uniswap_v3_pool_refactoring/architecture_diagram.md
&#8594; Read .opencode/plans/work-plan-aave-todos.md
&#8594; Read .opencode/plans/organization_improvement.md
&#8594; Read .opencode/plans/bootstrap_system.md
&#8226; Review hybrid configuration plan Explore Agent
&#8226; Review Aave transaction refactor plan Explore Agent
&#8226; Review Uniswap V3 pool refactoring plan Explore Agent
&#8226; Review Aave TODO work plan Explore Agent
&#8226; Review organization improvement plan Explore Agent
&#8226; Review bootstrap system plan Explore Agent

Now I'll delegate review tasks for each plan to assess progress against the referenced code and provide recommendations.

&#10003; Review hybrid configuration plan Explore Agent
&#10003; Review Aave transaction refactor plan Explore Agent

[...]</code></pre><p>The result is a nice summary that I can review each morning before starting work. This can be easily triggered by a systemd script or a cron job:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9xWm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9xWm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!9xWm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!9xWm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!9xWm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9xWm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png" width="1456" height="2035" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2035,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:281269,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/187546090?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9xWm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!9xWm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!9xWm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!9xWm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F668e8a12-f940-4376-836c-59d2c1659366_1564x2186.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Transaction Inspection</h3><p>My next automation was more complex. I found that during debugging, I was often repeating instructions for the same set of actions: inspect an Etherscan page, tell me what happened during a particular transaction, tell me which contracts were used.</p><p>So I defined a read-only single task <a href="https://opencode.ai/docs/agents#subagents">subagent</a> that would do these things:</p><p><code>evm-investigator.md</code></p><pre><code>---
description: Analyzes Ethereum or EVM-compatible blockchain transactions, accounts, and smart contracts.
mode: subagent
tools:
  write: false
  edit: false
  task: false
---

You are in Ethereum Virtual Machine (EVM) transaction investigation mode. Focus on:
- Smart contract source code
- Inspecting transactions using a block explorer (Etherscan or similar)
- User calldata
- Event logs
- Storage states
- Transaction tracing
- Function call arguments and return values</code></pre><p>Within the terminal user interface (TUI), trigger its use by mentioning it with the @ symbol:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!krtH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!krtH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!krtH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!krtH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!krtH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!krtH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png" width="1456" height="2035" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2035,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:269142,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/187546090?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!krtH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!krtH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!krtH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!krtH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be8ea04-c8f5-4d3d-802b-13290b7dc976_1564x2186.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Pretty cool!</p><h3>Automating Debugging</h3><p>It&#8217;s also simple to write a command that glues the output of something run in a terminal to a subagent. This command runs my Aave updater after a failure, instructs the model to delegate investigating the transaction that caused it, then start a hands-off debugging effort that records lessons learned to a log:</p><p><code>aave_debug.md</code></p><pre><code>---
description: Debug Aave update failures
agent: build
model: synthetic/hf:moonshotai/Kimi-K2.5
---

!`uv run degenbot aave update --no-progress-bar --one-chunk`

## DIRECTION: Investigate and debug this failed Aave update command

## PROCESS:
### 1. Gather Information
- Parse the output to identify the transaction hash associated with the event triggering the processing error
- Inspect the transaction using @evm-investigator

### 2. Investigate Code
- Review `aave_update` and determine the execution path leading to the error
- Check for related issues involving this error or a common execution path
- Generate a failure hypothesis

### 3. Validate Execution Path and Failure Hypotheses
- If the execution path is unclear, apply the `log_function_call` decorator to confirm function calls, e.g.,
    ```python
    @log_function_call
    def some_func(...): ...
    ```
- Determine if a debugging env var is useful:
    - `DEGENBOT_VERBOSE_USER=0x123...,0x456...`
    - `DEGENBOT_VERBOSE_TX=0xabc...,0xdef...`
    - `DEGENBOT_VERBOSE_ALL=1`
    - `DEGENBOT_DEBUG=1`
    - `DEGENBOT_DEBUG_FUNCTION_CALLS=1`
- Run with any verbosity flags prepended, e.g., `DEGENBOT_DEBUG=1 uv run degenbot aave update --no-progress-bar --one-chunk`

### 4. Fix &amp; Validate
- If a hypothesis is validated and the root cause is clear, implement a fix and run the update again

### 5. Document Findings
Append to @aave_debug_progress.md. Follow this format:
- **Issue:** Brief title
- **Date:** Current date
- **Symptom:** Error message verbatim
- **Root Cause:** Technical explanation
- **Transaction Details:** Hash, block, type, user, asset
- **Fix:** Code location and changes
- **Key Insight:** Lesson learned for future debugging
- **Refactoring:** Concise summary of proposed improvements to code that processes these transactions</code></pre><p>It&#8217;s now available in the TUI as slash command:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zQUx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zQUx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!zQUx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!zQUx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!zQUx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zQUx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png" width="1456" height="2035" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2035,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:81001,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/187546090?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zQUx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!zQUx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!zQUx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!zQUx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd1326ee-562c-43db-9242-3ed45d58fb32_1564x2186.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>After running for a minute, it narrowed down and identified the bug:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lBpA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lBpA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!lBpA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!lBpA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!lBpA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lBpA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png" width="1456" height="2035" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2035,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:272684,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/187546090?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lBpA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!lBpA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!lBpA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!lBpA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10bcef8-e1e4-4dd1-9106-3e5cdc0e8819_1564x2186.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And then gives me a nice little summary of what it did:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SdQE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SdQE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!SdQE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!SdQE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!SdQE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SdQE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png" width="1456" height="2035" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2035,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:331444,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/187546090?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SdQE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png 424w, https://substackcdn.com/image/fetch/$s_!SdQE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png 848w, https://substackcdn.com/image/fetch/$s_!SdQE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png 1272w, https://substackcdn.com/image/fetch/$s_!SdQE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85aecc8f-6001-4f4c-a79c-121f9050f4ed_1564x2186.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Unattended Ralph Loop</h3><p>Here&#8217;s where we max the technique out, setting up an unattended development loop where your agent can develop new features, make fixes, do code review, and run experiments while you sleep.</p>
      <p>
          <a href="https://www.degencode.com/p/agentic-coding">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Aave — Part VI]]></title><description><![CDATA[Adding Positions to the Database]]></description><link>https://www.degencode.com/p/aave-part-vi-adding-db-positions</link><guid isPermaLink="false">https://www.degencode.com/p/aave-part-vi-adding-db-positions</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Wed, 31 Dec 2025 23:47:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!VdaR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d418852-256e-4ecc-bce9-2065ed7047d8_2730x1890.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Previous entries in the <a href="https://www.degencode.com/t/aave">Aave Series</a> focused on understanding the Aave platform and the necessary contract calls to identify users, inspect their positions, and perform liquidations.</p><p>Now let&#8217;s zoom out and build a means to monitor these positions over the long term.</p><p>We will focus on developing a database structure for Aave V3 using <a href="https://www.sqlalchemy.org/">SQLAlchemy</a>, writing an extractor function to pull the necessary events and update the database, then run some limited tests to verify the data behaves as expected.</p><p>This entry is heavily influenced by ideas from <a href="https://en.wikipedia.org/wiki/Domain-driven_design">Domain-Driven Design</a>. If you find it useful, consider reading the books &#8220;Domain-Driven Design: Tackling Complexity in the Heart of Software&#8221; by Eric Evans and &#8220;Implementing Domain-Driven Design&#8221; by Vernon Vaughan.</p>
      <p>
          <a href="https://www.degencode.com/p/aave-part-vi-adding-db-positions">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Aave — Part V]]></title><description><![CDATA[Liquidation Valuation & User Asset Discovery]]></description><link>https://www.degencode.com/p/aave-part-v-liquidation-valuation-user-asset-discovery</link><guid isPermaLink="false">https://www.degencode.com/p/aave-part-v-liquidation-valuation-user-asset-discovery</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Thu, 18 Dec 2025 00:10:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!dPPk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c245f6-f959-49b3-a48d-d60ca0565576_1962x1104.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We discovered in <a href="https://www.degencode.com/p/aave-part-iv-borrower-discovery-position-inspection">Part IV</a> that liquidatable positions are frequently present on Aave. But it&#8217;s not enough to find all the positions below the liquidation threshold and clear them out &#8212; position size and gas fees factor into the overall profit. </p><p>An Aave user can have multiple collateral deposits and borrows across the platform. But Aave tracks only that user&#8217;s overall health factor. We saw this when calling the <code>getUserAccountData</code> function on the <a href="https://www.degencode.com/p/aave-part-ii-pool-contract">Pool contract</a>. </p><p>The collateral, debt, and available borrowing capacity values reported by that function are expressed in terms of a common base currency. This base currency is not defined by Aave, it is largely a function of the oracles used for each reserve asset. For example, to determine the collateral value of WETH against the debt value of USDC, an intermediate base currency is used. The Mainnet market uses USD as this intermediate.</p><p>So we must always be aware that Aave measures user health against this base currency. Aave reserve assets move against USD and against each other, which makes accounting more complex.</p><p>Example: if you&#8217;re considering a liquidation of a WBTC loan backed by WETH, you must be aware of at least <strong>three</strong> prices:</p>
      <p>
          <a href="https://www.degencode.com/p/aave-part-v-liquidation-valuation-user-asset-discovery">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Aave — Part IV]]></title><description><![CDATA[Borrower Discovery, Position Inspection]]></description><link>https://www.degencode.com/p/aave-part-iv-borrower-discovery-position-inspection</link><guid isPermaLink="false">https://www.degencode.com/p/aave-part-iv-borrower-discovery-position-inspection</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Wed, 03 Dec 2025 14:31:25 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G6XT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Loan positions on Aave can be easily inspected, provided that you know who has them.</p><p>There is no onchain view that exposes a simple list of active loan positions or their users, so we&#8217;ll need to dig through transaction logs to discover the borrowers and look up their positions.</p><p>Each new loan operation emits a <code>Borrow</code> event, which is defined in the <a href="https://github.com/aave-dao/aave-v3-origin/blob/main/src/contracts/interfaces/IPool.sol">IPool.sol</a> interface:</p><pre><code>event Borrow(
    address indexed reserve,
    address user,
    address indexed onBehalfOf,
    uint256 amount,
    DataTypes.InterestRateMode interestRateMode,
    uint256 borrowRate,
    uint16 indexed referralCode
);</code></pre><p>Using Ape, connect to Ethereum mainnet and query for <code>Borrow</code> events starting at block 23,000,000:</p><pre><code>btd@dev:~$ ape console --network :mainnet
INFO:     Connecting to existing Reth node at http://localhost:8545.

In [1]: pool = Contract('0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2')

In [2]: borrows = pool.Borrow.query('*', start_block=23_000_000)

In [3]: borrows
Out[3]: 
0       0x807c5ad511680ecb97cc3a9f9caaacb6f55c98799596... 
1       0xa40948b206cde4493e8772bb9cf2dd38ef020151a453... 
2       0x8ed9da1f0a1c0daedd0b1c7a98811078cb09945076e0... 
3       0xab6b82ec1d7df25fcf8826d9410013d7813b9c5fb514... 
4       0x6f8dd67ab48078d522144126929d194d12b49b89196c... 
...
...
191965  0xc24c257bbdfd99a3693912169190f863874c134e2ec0... 
191966  0xf906e5d7b5926444dabd3750a510df36e13658433383... 
191967  0x8c0a4488312fda581f4a5d01e7bab4446d8aa9412628... 
191968  0x893e30290be406185abc59cabfef540335f53428a80d... 
191969  0xcd5c580418e3cb18cd5b2e730f671459dd90cf03e63e... 

[191970 rows x 8 columns]</code></pre><p>Calls to <code>query()</code> return a <a href="https://pandas.pydata.org/">Pandas</a> DataFrame with columns corresponding to the event&#8217;s position in the block, its transaction, and the data in the event itself. You can view these via the <code>columns</code> attribute on the DataFrame:</p><pre><code>In [4]: borrows.columns
Out[4]: 
Index(
    [
        &#8217;block_hash&#8217;, 
        &#8216;block_number&#8217;, 
        &#8216;contract_address&#8217;, 
        &#8216;event_arguments&#8217;,
        &#8216;event_name&#8217;, 
        &#8216;log_index&#8217;, 
        &#8216;transaction_hash&#8217;, 
        &#8216;transaction_index&#8217;
    ],
    dtype=&#8217;object&#8217;
)</code></pre>
      <p>
          <a href="https://www.degencode.com/p/aave-part-iv-borrower-discovery-position-inspection">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Aave — Part III]]></title><description><![CDATA[Supply, Withdraw, Borrow, Repay, and Liquidate]]></description><link>https://www.degencode.com/p/aave-part-iii-supply-withdraw-borrow-repay-and-liquidate</link><guid isPermaLink="false">https://www.degencode.com/p/aave-part-iii-supply-withdraw-borrow-repay-and-liquidate</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Sat, 29 Nov 2025 00:29:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!oCHN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb229c5be-f292-4d9f-9864-b704b3c7202f_1491x1181.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The market is volatile and someone has to stand guard against bad debt. Will you answer the call, dear reader?</p><p>Continuing the <a href="https://www.degencode.com/t/aave">Aave Series</a>, let&#8217;s build to the ultimate goal of liquidating unhealthy debt positions.</p><p>We&#8217;ll cover the five primary functions of the Pool contract, ordered to build layered understanding until we reach our target: <code>supply</code>, <code>withdraw</code>, <code>borrow</code>, <code>repay</code>, and <code>liquidationCall</code>.</p><p>The lesson is structured as a series of tests using Ape Framework that interacts with each of the five contract functions, plus some accessory calls to other contracts that will support our efforts.</p><p>This entry assumes you are familiar with the Aave concepts and Pool contract covered in <a href="https://www.degencode.com/p/aave-part-ii-pool-contract">Part II</a>, how to set up Ape Framework and how to use it for executing tests against a forked chain.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;af932678-29d6-4215-a188-1a121a74b134&quot;,&quot;caption&quot;:&quot;The set of Aave contracts deployed for a given chain is known as a &#8220;market&#8221;. Each chain typically has a main market for users, though other markets may be deployed on that chain with different features.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Aave&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2025-11-25T15:31:20.472Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!1psL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/aave-part-ii-pool-contract&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:179837891,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;df24f386-43d5-4a9f-894d-aab1b03fa7b8&quot;,&quot;caption&quot;:&quot;An effective way to expand your skills is taking what you already know and attempting to do it with new tools.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Introduction to Ape Framework&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2023-10-08T23:50:40.436Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5ebe2e7c-a38a-4614-bdb4-af5b523e84c4_200x200.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/introduction-to-ape-framework&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:137754996,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:13,&quot;comment_count&quot;:6,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;483efc3a-4501-4e19-84b6-214f12eb2282&quot;,&quot;caption&quot;:&quot;I started out cowboy coding: write some new code, run it, see what happens. Nothing immediately failed? Assume it was good, write the next section of code.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Testing With Ape Framework&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2024-02-28T14:31:07.957Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/testing-with-ape-framework&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:142089052,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>Project Setup</h2><p>I will be working from a new Ape project in <code>~/code/aave</code>.</p><p>Create a virtual environment:</p><pre><code>btd@dev:~/code/aave$ uv venv --python 3.13
Using CPython 3.13.8
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate</code></pre><p>Install Ape with the set of recommended plugins:</p><pre><code>btd@dev:~/code/aave$ uv pip install eth-ape[recommended-plugins]
...
Installed 133 packages in 420ms
...</code></pre><p>Create the Ape project using <code>uv run ape init</code> and edit the configuration so that the default network used by tests is a local fork from Ethereum mainnet using Foundry:</p><p><code>ape-config.yaml</code></p><pre><code>name: Aave Pool

default_ecosystem: ethereum

ethereum:
  default_network: mainnet-fork
  mainnet:
    default_provider: node
  mainnet_fork:
    default_provider: foundry

node:  
  ethereum:
    mainnet:
      uri: http://localhost:8545 # EDIT THIS

plugins:
  - foundry
  - etherscan</code></pre><p>Create a connection test to confirm that <code>ape test</code> launches the fork from the correct chain:</p><p><code>tests/test_aave_pool.py</code></p><pre><code>from ape.managers.networks import NetworkManager
from ape_foundry.provider import FoundryForkProvider

def test_fork_is_launched(networks: NetworkManager):
    assert isinstance(networks.provider, FoundryForkProvider)
    assert networks.provider.chain_id == 1</code></pre><p>Execute using <code>uv run ape test</code> and confirm that test passes. Now we can be confident that tests will run against the fork.</p><h2>supply</h2><p>The <code>supply</code> function from <a href="https://github.com/aave-dao/aave-v3-origin/blob/main/src/contracts/protocol/pool/Pool.sol">Pool.sol</a>:</p><pre><code>function supply(
    address asset,
    uint256 amount,
    address onBehalfOf,
    uint16 referralCode
) public virtual override {
    SupplyLogic.executeSupply(
        _reserves,
        _reservesList,
        _usersConfig[onBehalfOf],
        DataTypes.ExecuteSupplyParams({
        user: _msgSender(),
        asset: asset,
        interestRateStrategyAddress: RESERVE_INTEREST_RATE_STRATEGY,
        amount: amount,
        onBehalfOf: onBehalfOf,
        referralCode: referralCode
        })
    );
}</code></pre><p>We need to provide four inputs:</p><ul><li><p>asset &#8212; the supplied asset address</p></li><li><p>amount &#8212; how much we will supply</p></li><li><p>onBehalfOf &#8212; the user address to credit the supply</p></li><li><p>referralCode &#8212; a unique referral code (set to zero, the referral program is currently inactive)</p></li></ul><p>We will be using WETH in various tests, so let&#8217;s make a fixture that creates a contract instance for use in other tests:</p><pre><code>WETH_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"

@pytest.fixture
def weth():
    return Contract(WETH_ADDRESS)</code></pre><p>And we&#8217;ll need a tester account with a balance:</p><pre><code>@pytest.fixture
def tester_account(accounts):
    return accounts[0]</code></pre><p>Create a test that asserts that <code>tester_account</code> has a balance, and that wrapping at the WETH contract works as expected:</p><pre><code>def test_wrap_ether(weth: Contract, tester_account):
    starting_ether_balance = tester_account.balance
    starting_weth_balance = weth.balanceOf(tester_account)

    deposit_amount = 100 * 10**18
    assert starting_ether_balance &gt; deposit_amount

    weth.deposit(value=deposit_amount, sender=tester_account)
    assert weth.balanceOf(tester_account) == (
        deposit_amount + starting_weth_balance
    )
    assert tester_account.balance &lt; starting_ether_balance</code></pre><p>Now we can write a test that wraps 100 Ether, approves it for use by the Pool contract, and supplies it:</p><pre><code>AAVE_V3_POOL = &#8220;0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2&#8221;

@pytest.fixture
def aave_pool():
    return Contract(AAVE_V3_POOL)


def test_supply(aave_pool: Contract, weth: Contract, tester_account):
    deposit_amount = 100 * 10**18
    weth.deposit(value=deposit_amount, sender=tester_account)
    weth.approve(
        aave_pool.address, 
        deposit_amount, 
        sender=tester_account
    )

    starting_weth_balance = weth.balanceOf(tester_account)
    tx = aave_pool.supply(
        weth.address, 
        deposit_amount, 
        tester_account.address, 
        0, 
        sender=tester_account
    )
    assert weth.balanceOf(tester_account) == (
        starting_weth_balance - deposit_amount
    )</code></pre><p>The test works as expected, but doesn&#8217;t tell us much. Add a simple <code>tx.show_trace()</code> at the end and run the test with <code>-s</code> to see the output:</p>
      <p>
          <a href="https://www.degencode.com/p/aave-part-iii-supply-withdraw-borrow-repay-and-liquidate">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Aave — Part II]]></title><description><![CDATA[Pool Contract]]></description><link>https://www.degencode.com/p/aave-part-ii-pool-contract</link><guid isPermaLink="false">https://www.degencode.com/p/aave-part-ii-pool-contract</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Tue, 25 Nov 2025 15:31:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!1psL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The set of Aave contracts deployed for a given chain is known as a &#8220;market&#8221;. Each chain typically has a main market for users, though other markets may be deployed on that chain with different features.</p><p>The largest market is on Ethereum mainnet (chain ID 1), accessible via the <a href="https://app.aave.com/">Aave front-end</a> by going to the <a href="https://app.aave.com/markets/">Markets</a> tab and choosing &#8220;Core&#8221; from the drop-down menu.</p><p>Many assets are enabled for this market, including the familiar <a href="https://app.aave.com/reserve-overview/?underlyingAsset=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2&amp;marketName=proto_mainnet_v3">Wrapped ETH</a>, <a href="https://app.aave.com/reserve-overview/?underlyingAsset=0xdac17f958d2ee523a2206206994597c13d831ec7&amp;marketName=proto_mainnet_v3">Tether USD</a>, and <a href="https://app.aave.com/reserve-overview/?underlyingAsset=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&amp;marketName=proto_mainnet_v3">USD Coin</a> tokens.</p><p>We will use this market for the remainder of our study. Be sure you connect to an appropriate RPC and use <a href="https://etherscan.io/">Etherscan</a> (or equivalent) as your block explorer.</p><p>It&#8217;s nice to see some data through the front-end, but our primary focus is on the contracts.</p><h2>Concepts</h2><p>Aave has several domain-specific terms and concepts. Not all of these concepts are relevant to us, but some merit a brief overview.</p><h3>Liquidity Pool</h3><p>Uniswap has the concept of individual liquidity pools holding a pair of assets. Each pool has a distinct contract address that holds the balance of each assets.</p><p>Aave refers to an instance of a market (and all of its related contracts) as a liquidity pool. Assets are not held by a single contract, rather they are managed by several distinct ones.</p><h3>Reserve</h3><p>A reserve is a generic name for a token held by an Aave liquidity pool. Individual reserve assets are approved and enabled by the protocol for use. The characteristics of each asset vary, so reserves have several parameters to control how they can be used by the protocol:</p><ul><li><p>Loan-to-Value (LTV) &#8212; a percentage specifying the maximum size of the debt that can be created against the reserve</p></li><li><p>Liquidation Threshold &#8212; a percentage specifying the LTV where a position is considered undercollateralized and eligible for liquidation</p></li><li><p>Borrowing Enabled &#8212; true/false</p></li><li><p>Caps &#8212; upper limits on both the supplied and borrowed amounts</p></li><li><p>Interest Rate Model &#8212; parameters to set the shape of the interest rate curve</p></li></ul><h3>aToken</h3><p>An <a href="https://aave.com/docs/aave-v3/smart-contracts/tokenization#atoken">aToken</a> is an ERC-20 that represents a claim on some Aave reserve asset. When a liquidity provider supplies an asset, an amount of some aToken is minted to them. To withdraw that asset, the corresponding aToken is burned and the asset is returned.</p><p>The aToken contract is standalone and holds its underlying asset. The asset can be withdrawn only by the Pool contract calling <code>burn</code> on behalf of a user.</p><h3>vToken</h3><p>A <a href="https://aave.com/docs/aave-v3/smart-contracts/tokenization#variabledebttoken">vToken</a> is an ERC-20 that represents a variable rate debt position. It is non-transferable and used to track debts for by a particular user. </p><h3>Interest Rate</h3><p>The interest rate is a percentage of the loan amount that must be repaid by borrowers to close their debt position. There are two interest rates: one for borrowing; one for supplying. The rate for borrowing is higher than the rate for supplying, and the difference is kept as a fee to the protocol.</p><p>Each interest rate curve is upward sloping with respect to usage ratio, the fraction of all open debt positions against the collateral. The interest rate has two slopes. The higher of the two is used after the LTV for the market crosses an optimal point.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1psL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1psL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png 424w, https://substackcdn.com/image/fetch/$s_!1psL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png 848w, https://substackcdn.com/image/fetch/$s_!1psL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png 1272w, https://substackcdn.com/image/fetch/$s_!1psL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1psL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png" width="1060" height="688" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:688,&quot;width&quot;:1060,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50256,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/179837891?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1psL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png 424w, https://substackcdn.com/image/fetch/$s_!1psL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png 848w, https://substackcdn.com/image/fetch/$s_!1psL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png 1272w, https://substackcdn.com/image/fetch/$s_!1psL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa47e1f53-654a-4d49-aa46-9b71361f74a6_1060x688.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The net effect of this dual-slope strategy is that loan creation is encouraged by low borrowing rates when LTV is low, and discouraged by high borrowing rates when LTV is high. </p><p>Since interest rates are continuously variable, the total interest paid to a liquidity provider is calculated instantaneously at withdrawal time.</p><p>Liquidity providers are paid their accumulated interest through a scaling mechanism whereby their share of the reserve asset is multiplied by a ratio corresponding to the current liquidity index which is dynamically adjusted. </p><h3>Utilization Rate</h3><p>Each asset has a target utilization rate set by protocol governance. The protocol-wide LTV for a particular asset is rolled into this value. The transition between the two  slopes on the interest rate curve occurs at this target optimal utilization rate.</p><p>This value is shown on the front-end, and can also be read from the asset&#8217;s holding contract.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IrLr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IrLr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png 424w, https://substackcdn.com/image/fetch/$s_!IrLr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png 848w, https://substackcdn.com/image/fetch/$s_!IrLr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png 1272w, https://substackcdn.com/image/fetch/$s_!IrLr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IrLr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png" width="1456" height="601" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:601,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44584,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/179837891?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IrLr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png 424w, https://substackcdn.com/image/fetch/$s_!IrLr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png 848w, https://substackcdn.com/image/fetch/$s_!IrLr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png 1272w, https://substackcdn.com/image/fetch/$s_!IrLr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7978fd24-9890-4f33-b2ce-371ffcf97949_1475x609.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Interest rate curve for Wrapped ETH</figcaption></figure></div><h3>Efficiency Mode (E-Mode)</h3><p>For correlated assets like stablecoins or ETH derivates, Aave provides a special mode for borrowing that allows for higher LTVs.</p><h3>Isolation Mode</h3><p>The protocol is at maximum risk when a volatile asset backs a loan for another volatile asset.</p><p>Nevertheless, some new assets may be desirable, so Aave allows them to be used as collateral against less volatile assets only. A typical use case is allowing a new volatile asset to be supplied as collateral for loans of stablecoins.</p><h2>Console Exploration</h2><p>Throughout the series, console code will be provided. The underlying tool is Ape Framework, most often connected to an Ethereum mainnet RPC. </p>
      <p>
          <a href="https://www.degencode.com/p/aave-part-ii-pool-contract">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Aave — Part I]]></title><description><![CDATA[Introduction to Lending Protocols]]></description><link>https://www.degencode.com/p/aave-part-i-introduction-to-lending-protocols</link><guid isPermaLink="false">https://www.degencode.com/p/aave-part-i-introduction-to-lending-protocols</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Sat, 22 Nov 2025 00:02:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!MZ5a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A lending protocol is a set of smart contracts that facilitate loans against a pool of user-supplied deposits. </p><p>The individual mechanisms are simple:</p><ul><li><p>A depositor can supply an asset to the protocol</p></li><li><p>A depositor can withdraw their previously supplied asset, plus accumulated interest, up to the unborrowed amount currently held by the protocol</p></li><li><p>A depositor can also be a borrower, and can create a loan against some other assets</p></li><li><p>Borrowers must repay the full amount of their loan, plus accumulated interest, to close their loan</p></li></ul><p>The value of a deposit must exceed the loan amount at the time of generation, a practice known as &#8220;overcollaterilization&#8221;. This provides a safety margin so the protocol and its users can react appropriately to fluctuating collateral values.</p><h2>Bad Debt</h2><p>Since the protocol distributes assets deposited by its users, there is a risk of insolvency if loans are not repaid.</p><p>For a given loan, if the value of its collateral assets falls below the value of the loaned assets, it is unlikely that the borrower will repay it &#8212; a rational borrower would prefer to hold the loaned assets instead of returning it for their devalued deposit. Loans like this are labeled &#8220;bad debt&#8221; because they create unsatisfied obligations to two groups of depositors &#8212; one supplying the collateral asset, and one supplying the loaned asset.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MZ5a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MZ5a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!MZ5a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!MZ5a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!MZ5a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MZ5a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg" width="1408" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:759046,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.degencode.com/i/179575540?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MZ5a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!MZ5a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!MZ5a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!MZ5a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F795ecba0-a9a2-4590-976e-bd5a37930383_1408x768.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Bad debt hurts both groups because a deposit asset can only be withdrawn up to the unborrowed amount &#8212; that is, only the amount of an asset held by the pool and not currently given to a borrower. </p><p>In the case of excessive bad debt, a lending protocol can experience a &#8220;bank run&#8221; event where only a fraction of users can withdraw unborrowed assets, and everyone after is unable to withdraw.</p><h2>Liquidations</h2><p>Since bad debt is dangerous, the protocol needs safeguards and mechanisms to reduce or remove bad debt. </p><p>Some protocols manage a separate asset reserve that can make depositors whole following a liquidity event. This is outside the scope of our study, so we won&#8217;t concern ourselves with this.</p><p>The first defense against accumulating bad debt is to incentivize third parties to liquidate (repay) unhealthy loans. As the asset value ratio of collateral vs. borrowed falls, liquidators are incentivized to repay a portion of the loan in exchange for an inflated share of the collateral. </p><p>The primary effect is reducing the outstanding obligation to the loaned asset&#8217;s depositors. But the secondary effect is <em>increasing</em> the outstanding obligation to the collateral asset&#8217;s depositors.</p><p>These effects seemingly antagonize the two groups &#8212; the loaned asset&#8217;s depositors are being made whole at the cost of the collateral asset&#8217;s depositors!</p><p>However, remember that every loan is overcollateralized. Thus each collateral asset across the protocol should have a net positive amount of pooled liquidity. This extra balance is what pays for the liquidation incentive, and lending protocols often implement variable interest rates to encourage users to shift their behavior towards either deposits or borrowing.</p><h2>Aave</h2><p>According to <a href="https://defillama.com/protocols/lending">DefiLlama</a>, <a href="https://aave.com/">Aave</a> is the largest lending protocol by total volume locked (TVL).</p><p>There are three major versions of Aave, presented below with a link to the white paper, date of first deployment to Ethereum mainnet, and a snapshot of TVL:</p><ul><li><p><a href="https://github.com/aave/aave-protocol/blob/master/docs/Aave_Protocol_Whitepaper_v1_0.pdf">V1</a> &#8212; Jan 2020, <a href="https://defillama.com/protocol/aave-v1">$10.3 million</a></p></li><li><p><a href="https://github.com/aave/protocol-v2/blob/master/aave-v2-whitepaper.pdf">V2</a> &#8212; Dec 2020, <a href="https://defillama.com/protocol/aave-v2">$187.8 million</a></p></li><li><p><a href="https://github.com/aave-dao/aave-v3-origin/blob/main/docs/Aave_V3_Technical_Paper.pdf">V3</a> &#8212; March 2022, <a href="https://defillama.com/protocol/aave-v3">$30.3 billion</a></p></li></ul><p>Aave V3 manages a majority of current lending activity, so we will focus on it. V3 is deployed to several chains, so the strategies we develop here will be portable.</p><h2>Moving Forward</h2><p>We are here to find profitable opportunities, and the primary way to do this on Aave is to act as a liquidator.</p><p>But before we can develop a liquidation strategy we must study: how deposits are managed; how loans are created, repaid, and closed; and how loan health is tracked.</p><p>The next entry in the series will cover the most important smart contract on Aave &#8212; <a href="https://aave.com/docs/aave-v3/smart-contracts/pool">Pool</a>.</p>]]></content:encoded></item><item><title><![CDATA[Release: 0.5.0]]></title><description><![CDATA[One More Note About Pathfinding]]></description><link>https://www.degencode.com/p/release-050</link><guid isPermaLink="false">https://www.degencode.com/p/release-050</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Thu, 20 Nov 2025 22:01:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G6XT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The path finders are integrated into <a href="https://pypi.org/project/degenbot/">degenbot</a> and I will be publishing 0.5.0 shortly &#8212; hooray! It&#8217;s been a long road but I&#8217;m pleased with the database integration, pathfinding improvements, removing the soft dependency on Cryo for chain data extraction, and needing to statically generate JSON pool data and arbitrage paths.</p><p>You should see it hit PyPi today once I finalize some release checks and final tests. As always, install it with <code>pip</code> or <code>uv</code>.</p><h2>Multiple Routes</h2><p>In the <a href="https://www.degencode.com/p/new-features-uniswap-v4-database-integration-and-path-finding">previous post</a> I mentioned the ability to provide a mapping between equivalent tokens. However as I continued scaling the path finder to work for arbitrary depth path searches, I realized it was clunky and refactored it.</p><p>I ultimately refactored the equivalent tokens map with the more general ability to specify a list of start and end tokens. This accomplishes the same goal while simplifying the control flow.</p><p>Here&#8217;s a sample function call taken from a test:</p><pre><code>def test_generic_algo_multiple_tokens():
    depth = 2

    # UniswapV4 pools hold both native and WETH pairs, so paths to 
    # and from both tokens can be found using this single type
    pool_types: list[type] = [UniswapV4PoolTable]

    generic_paths_weth_to_weth = list(
        find_paths(
            chain_id=BASE_CHAIN_ID,
            start_tokens=[WETH_BASE_ADDRESS],
            end_tokens=[WETH_BASE_ADDRESS],
            max_depth=depth,
            pool_types=pool_types,
        )
    )

    generic_paths_weth_to_native = list(
        find_paths(
            chain_id=BASE_CHAIN_ID,
            start_tokens=[WETH_BASE_ADDRESS],
            end_tokens=[ZERO_ADDRESS],
            max_depth=depth,
            pool_types=pool_types,
        )
    )

    generic_paths_weth_to_weth_or_native = list(
        find_paths(
            chain_id=BASE_CHAIN_ID,
            start_tokens=[WETH_BASE_ADDRESS],
            end_tokens=[WETH_BASE_ADDRESS, ZERO_ADDRESS],
            max_depth=depth,
            pool_types=pool_types,
        )
    )

    assert len(
        generic_paths_weth_to_weth_or_native
    ) == len(
        generic_paths_weth_to_weth
    ) + len(
        generic_paths_weth_to_native
    )</code></pre><p>In this test, three distinct path finding searches are performed through the set of known Uniswap V4 pools on Base mainnet: </p><ul><li><p>WETH &#8594; WETH</p></li><li><p>WETH &#8594; Ether</p></li><li><p>WETH &#8594; WETH | Ether (a hybrid search with multiple destinations)</p></li></ul><p>At the end, the number of paths found by the hybrid search is confirmed to match the sum of the paths found by the two single-token searches. </p><p>I also included a more complex assertion after that that confirms that all of the paths match exactly, but that required some sorting and transformation, so it&#8217;s not relevant here.</p><p>You can also run a multiple start / multiple end search:</p><pre><code>generic_paths_weth_or_native_to_weth_or_native = list(
    find_paths(
        chain_id=BASE_CHAIN_ID,
        start_tokens=[WETH_BASE_ADDRESS, ZERO_ADDRESS],
        end_tokens=[WETH_BASE_ADDRESS, ZERO_ADDRESS],
        pool_types=pool_types,
    )
)</code></pre><p>Ultimately this searches four routes, returning results as the <a href="https://en.wikipedia.org/wiki/Cartesian_product">Cartesian product</a> of the inputs:</p><ul><li><p>WETH &#8594; WETH</p></li><li><p>WETH &#8594; Ether</p></li><li><p>Native Ether &#8594; WETH</p></li><li><p>Native Ether &#8594; Ether</p></li></ul><h2>Variable Depth</h2><p>The default argument to <code>find_paths</code> will yield all paths with minimum depth equal to 2. A path&#8217;s depth is measured by the number of pools it contains. </p><p>There is an optional maximum depth argument, which defaults to <code>None</code>! Beware of leaving this at the default, because the search will continue &#8220;forever&#8221; if allowed. </p><p>There is a finite limit on the number of routes through a graph since an edge (pool) can only be used once, but for practical purposes you should always set the maximum depth to a value between 2-4. The number of possible routes increases exponentially as you increase depth.</p><pre><code><code># Find 2-pool &amp; 3-pool paths
find_paths(
    chain_id=BASE_CHAIN_ID,
    start_tokens=[WETH_BASE_ADDRESS],
    end_tokens=[WETH_BASE_ADDRESS],
    min_depth=2,
    max_depth=3,
)</code></code></pre><p>To perform a fixed length search, set the minimum and maximum depth equal:</p><pre><code># Find only 2-pool paths
find_paths(
    chain_id=BASE_CHAIN_ID,
    start_tokens=[WETH_BASE_ADDRESS],
    end_tokens=[WETH_BASE_ADDRESS],
    min_depth=2,
    max_depth=2,
)

# Find only 3-pool paths
find_paths(
    chain_id=BASE_CHAIN_ID,
    start_tokens=[WETH_BASE_ADDRESS],
    end_tokens=[WETH_BASE_ADDRESS],
    min_depth=3,
    max_depth=3,
)</code></pre><p>As a curiosity, Uniswap V4 allows for a WETH-Ether pair, which technically satisfies a mixed asset 1-pool path. Since this pool is a special case and a bit of a nuisance (deposits and withdrawals through the Wrapped Ether contract are always 1:1), I implicitly exclude it by setting the default depth to 2.</p><p>It&#8217;s usually not what you want, but in the interest of flexibility you can set the minimum depth to 1.</p><h2>Moving Forward</h2><p>With all this wrapped I&#8217;m putting two related topics on the roadmap: liquidations and lending protocols, beginning with Aave.</p>]]></content:encoded></item><item><title><![CDATA[New Features: Uniswap V4 Database Integration & Path Finding]]></title><description><![CDATA[Getting Ready for 0.5.0]]></description><link>https://www.degencode.com/p/new-features-uniswap-v4-database-integration-and-path-finding</link><guid isPermaLink="false">https://www.degencode.com/p/new-features-uniswap-v4-database-integration-and-path-finding</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Thu, 13 Nov 2025 19:53:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G6XT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have published a new beta of degenbot, <a href="https://pypi.org/project/degenbot/0.5.0b4/">0.5.0b4</a> which adds two key features.</p><h2>Uniswap V4</h2><p>The <a href="https://github.com/BowTiedDevil/degenbot/blob/main/src/degenbot/uniswap/v4_liquidity_pool.py">UniswapV4Pool</a> class now performs a lookup from the database at construction time. If you have activated and updated either of the built-in exchanges (<code>base_uniswap_v4</code> or <code>ethereum_uniswap_v4</code>), the class can skip many onchain calls needed to look up tokens, fee, tick spacing, etc.</p><p>Consequently many of the inputs to the construction are now optional. If you build a V4 pool that exists in the database, you only need to provide its pool manager address and pool ID. If you aren&#8217;t using the database, you can continue passing the values in as before.</p><h2>Path Finding</h2><p>I have also included an arbitrage path finding function <code>find_paths()</code>. It uses the database to discover pools and their tokens, then works through them to find pool paths in increasing depth. </p><p>This is a key unlock because it means that paths can be built during runtime, instead of needing to prebuild them with a separate script.</p><p>Here is an example of the simplest case, a two-pool cycle using some arbitrary profit token:</p><pre><code>for arb_path in find_paths(
    chain_id=8453,
    start_token=WETH_ADDRESS,
    end_token=WETH_ADDRESS,
    max_depth=2,
):
    for pool in arb_path:
        # [create pools and build the arbitrage helper here]</code></pre><p>The path finding function also supports limiting the pools to a subset of types that identify the database types:</p><pre><code><code>for arb_path in find_paths(
    chain_id=8453,
    start_token=WETH_ADDRESS,
    end_token=WETH_ADDRESS,
    max_depth=2,
    pool_types=[
        UniswapV2PoolTable,
        UniswapV4PoolTable,
    ],
):
    for pool in arb_path:
        # [create pools and build the arbitrage helper here]</code></code></pre><p>This is very useful if you have a specialized contract that is specific to V4-V2 arbs like this one I shared:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;6d1b2412-9b16-4489-9d09-92c2f3c4799e&quot;,&quot;caption&quot;:&quot;I deployed some rough V4/V2 arbitrage bots to Base for observation and testing early this week. They landed a few hundred tiny arbs &#8212; couch cushion money to be fair &#8212; but watching them helped me squish some bugs, and the system is functioning as expected. So let&#8217;s dig in!&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Project: Uniswap V4/V2 Two-Pool Arbitrage Bot&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2025-03-28T22:44:31.308Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!c9Qs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F267588e7-4bee-40c0-a0af-d6033452e21e_1024x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/project-uniswap-v4-v2-two-pool-arbitrage-bot&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:159928545,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:14,&quot;comment_count&quot;:0,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>It also accepts a list of equivalent tokens, for instance Ether &amp; WETH. This is very useful for cases where an execution contract can perform wrapping and unwrapping in between pools. This example would find all paths that take the forms WETH-X &#8594; Ether&#8594;X and Ether-X &#8594; WETH-X:</p><pre><code><code>for arb_path in find_paths(
    chain_id=8453,
    start_token=WETH_ADDRESS,
    end_token=V4_NATIVE_ADDRESS,
    max_depth=2,
    equivalent_tokens=[(WETH_ADDRESS, V4_NATIVE_ADDRESS)],
):
    for pool in arb_path:
        # [create pools and build the arbitrage helper here]</code></code></pre><p>In the case of two equivalent profit tokens, the ordering of the inputs is arbitrary. Reversing the start and end works as expected and yields the same pools.</p><p>Currently the only supported paths are two-pool, but I am working on pools of arbitrary length and will include that in the coming stable release.</p><h3>Talkin' 'bout My Generator</h3><p>The function is a <a href="https://docs.python.org/3/glossary.html#term-generator">generator</a> which must be consumed.</p><p>You can either loop through it using <code>for</code> or <code>next</code>, or capture all of the paths in a container (<code>tuple</code>, <code>list</code>, <code>deque</code>, etc). It&#8217;s simple enough once you&#8217;ve seen it, but don&#8217;t panic when a familiar operation like <code>len(find_paths(&#8230;))</code> or <code>find_paths(&#8230;)[0]</code> fails.</p><p>The generator approach has two key benefits:</p><ul><li><p>Path finding can start immediately &#8212; improving time-to-first-calculation</p></li><li><p>Memory use is minimized &#8212; each path is built and given directly to the consuming function instead of being held until the path search is complete</p></li></ul><h2>Final Release Soon</h2><p>I&#8217;m pleased with the new features in testing, and have already retired my Cryo extraction and two-pool path finding scripts. After I convert the remaining path finding scripts for 3 or more tokens, it should be much simpler to manage and consolidate all of this stuff.</p><p>This <em>should</em> be the final beta release ahead of 0.5.0, so please report bugs if you find them.</p>]]></content:encoded></item><item><title><![CDATA[New Feature: Mainnet Exchanges & Pool Updaters]]></title><description><![CDATA[Getting Ready for 0.5.0]]></description><link>https://www.degencode.com/p/new-feature-mainnet-exchanges-and</link><guid isPermaLink="false">https://www.degencode.com/p/new-feature-mainnet-exchanges-and</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Thu, 30 Oct 2025 20:36:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G6XT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hot on the heels of degenbot <a href="https://pypi.org/project/degenbot/0.5.0b2/">0.5.0b2</a>, I have published <a href="https://pypi.org/project/degenbot/0.5.0b3/">0.5.0b3</a> which adds a set of exchanges for Ethereum mainnet:</p><ul><li><p>Pancakeswap &#8212; V2, V3</p></li><li><p>Sushiswap &#8212; V2, V3</p></li><li><p>Uniswap &#8212; V2, V3, V4</p></li></ul><p>These can be activated and updated like the Base mainnet exchanges featured in the previous beta releases.</p><p>I am targeting two more features before the official 0.5.0 release: pathfinding directly from the database, and integrating database lookups into the Uniswap V4 pool class.</p>]]></content:encoded></item><item><title><![CDATA[Getting To Know The Database]]></title><description><![CDATA[Pool Lookups With SQLAlchemy & Simple Pathfinding]]></description><link>https://www.degencode.com/p/getting-to-know-the-database</link><guid isPermaLink="false">https://www.degencode.com/p/getting-to-know-the-database</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Mon, 27 Oct 2025 22:25:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!geBl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44730ac3-3847-4032-b39b-732340311d3e_1692x1706.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The <a href="https://www.degencode.com/p/new-feature-database-integration">database integration</a> is now available in the degenbot 0.5 beta. Instead of tossing it out there and hoping people figure it out, I&#8217;ll demonstrate and discuss some ways it can be used.</p><p>I encourage you to try out the beta release in a virtual environment and build a minimal database of V2 pools on Base mainnet, which is fast to populate with a fairly simple set of commands:</p><pre><code>degenbot activate base_aerodrome_v2
degenbot activate base_pancakeswap_v2
degenbot activate base_sushiswap_v2
degenbot activate base_swapbased_v2
degenbot activate base_uniswap_v2

# pause here to make sure an RPC is defined in config.toml and to update # database schema

degenbot pool update</code></pre><p>The V3 / V4 updaters will take much longer to run, and can be done later once you&#8217;re comfortable.</p><h2>SQLAlchemy</h2><p>I&#8217;ve used SQLAlchemy as the object-relational mapper (ORM) to translate the information in the database to our running applications. The classes are available under the <a href="https://github.com/BowTiedDevil/degenbot/tree/main/src/degenbot/database/models">degenbot.database.models</a> module. Any class that derives from <code>Base</code> defines a database table.</p><p>These classes have fairly straightforward names that suggest the information contained in each. </p><p>Examples:</p><ul><li><p><code>Erc20TokenTable</code> holds information for ERC-20 tokens</p><ul><li><p>Chain ID</p></li><li><p>Address</p></li><li><p>Decimals</p></li><li><p>Name</p></li><li><p>Symbol</p></li></ul></li><li><p><code>LiquidityPoolTable</code> is a polymorphic table class that holds a subset of information common to all liquidity pools, and a link to other subclasses with exchange-specific info as needed</p><ul><li><p>Chain ID</p></li><li><p>Address</p></li><li><p>Kind &#173;&#8212; a label which maps different liquidity pools associated with a particular exchange to separate tables</p><ul><li><p>An entry with kind &#8220;uniswap_v2&#8221; identifies this pool as Uniswap V2, and each entry in <code>UniswapV2PoolTable</code> will refer back to this base table with a foreign key.</p></li></ul></li><li><p>Token0 ID &amp; Token1 ID &#8212; both map to entries in <code>Erc20TokenTable</code> so that token specific data can be looked up via foreign key instead of duplicated in pool tables</p></li><li><p>Fee</p></li></ul></li><li><p><code>LiquidityPositionTable</code> holds information about Uniswap V3 liquidity positions</p><ul><li><p>Pool ID &#8212; a foreign key mapping the position to a given pool</p></li><li><p>Tick</p></li><li><p>Liquidity (Net)</p></li><li><p>Liquidity (Gross)</p></li></ul></li><li><p><code>UniswapV4PoolTable</code> holds information about Uniswap V4 liquidity pools. These are all managed by a single Pool Manager contract, so they all have a common address. Thus they are tracked independently of the pools defined in <code>LiquidityPoolTable</code>, which are uniquely identified by their address</p><ul><li><p>Pool Hash &#8212; referred to as the Pool ID by Uniswap, this is a 32 byte hash that uniquely identifies the pool at the Pool Manager</p></li><li><p>Hooks &#8212; an address that identifies the various hooks enabled by the pool</p></li><li><p>Currency0 ID &amp; Currency1 ID &#8212; like the ERC-20 references from above</p></li><li><p>Fee</p></li><li><p>Tick spacing</p></li><li><p>Kind &#8212; a label to identify the particular exchange</p></li></ul></li><li><p><code>ManagedPoolLiquidityPositionTable</code> holds liquidity positions for managed pools. Essentially identical to <code>LiquidityPositionTable</code> except that it points to managed pools in <code>ManagedLiquidityPoolTable</code> </p></li><li><p><code>PoolManagerTable</code> which holds information about individual Pool Manager contracts</p><ul><li><p>Chain ID</p></li><li><p>Address</p></li><li><p>Kind &#8212; a label to identify the particular exchange</p></li></ul></li></ul><h3>Relationships</h3><p>A clear win from using an ORM is that links between tables can be expressed as a relationship and accessed like an attribute, instead of having to write complex JOIN queries.</p>
      <p>
          <a href="https://www.degencode.com/p/getting-to-know-the-database">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[New Feature: Database Integration]]></title><description><![CDATA[So Long, and Thanks for All the Scripts]]></description><link>https://www.degencode.com/p/new-feature-database-integration</link><guid isPermaLink="false">https://www.degencode.com/p/new-feature-database-integration</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Wed, 15 Oct 2025 19:39:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!N9lF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd56f96be-3258-4938-9c71-f97607e95b75_2612x1710.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently aired my grievances about using JSON for liquidity mapping and proposed an alternative: <a href="https://sqlite.org/">SQLite</a>.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;b41ad2da-52be-47e0-8c73-ad9437cea43f&quot;,&quot;caption&quot;:&quot;It&#8217;s critical to learn how to efficiently store, retrieve, and process bulk data.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Performance Optimization: SQLite Cache&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:33970100,&quot;name&quot;:&quot;BowTiedDevil&quot;,&quot;bio&quot;:&quot;Self-taught coder, engineer, blockchain enthusiast, technical writer. &quot;,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/55abe25f-1bc7-46e5-94d4-7d109d5d3a3d_1620x1620.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2025-07-08T17:02:22.821Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!9YyO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95fb7de9-4439-4b82-a05b-618fddb0a8d9_2166x1488.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.degencode.com/p/performance-optimization-sqlite-cache&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:167741739,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:5,&quot;comment_count&quot;:0,&quot;publication_id&quot;:607913,&quot;publication_name&quot;:&quot;Degen Code&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!G6XT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F7268965e-ca71-4fa1-aaee-6387622ce3ca_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>I spent last week working on the feature. The integration is largely complete, so after some final QA I will publish a 0.5 beta version to PyPi. </p><p><s>If everything goes as expected, I should have it published in the next day or two. I will edit this post with a link when it&#8217;s ready.</s></p><p>The 0.5 beta release is <a href="https://pypi.org/project/degenbot/0.5.0b1/">available</a>. By default, pip will ignore alpha, beta, and release candidate versions &#8212; to install, run <code>pip install degenbot==0.5.0b2</code>. </p><h2>Command Line</h2><p>The 0.5 releases of degenbot added command line integration with a set of useful commands. </p>
      <p>
          <a href="https://www.degencode.com/p/new-feature-database-integration">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Rust For Pythonistas]]></title><description><![CDATA[Part IV: Threading & Concurrency]]></description><link>https://www.degencode.com/p/rust-for-pythonistas-part-iv-threading-and-concurrency</link><guid isPermaLink="false">https://www.degencode.com/p/rust-for-pythonistas-part-iv-threading-and-concurrency</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Mon, 29 Sep 2025 17:04:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/42f1a887-3712-4464-83e0-79b7d954c9e7_460x307.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The Rust Book devotes a chapter to what it calls <a href="https://doc.rust-lang.org/book/ch16-00-concurrency.html">&#8220;Fearless Concurrency&#8221;</a>. </p><p>If you&#8217;ve done concurrent programming using coroutines, threads, or multiple processes, you know how difficult it can be. You must balance protection from data races and corruption against the risk of deadlock and slowdown.</p><p>The Rust promise is that the ownership model mitigates the impact of both: the former because simultaneous read/write issues are prohibited; the latter because locks become <em>mostly</em> unnecessary for common concurrency patterns.</p><p>We will study how to spawn threads in Rust, the <code>Arc</code> and <code>Mutex</code> synchronization primitives, the <code>Sync</code> and <code>Send</code> traits, parallel iteration using <a href="https://github.com/rayon-rs/rayon">Rayon</a>, and async functions using <a href="https://tokio.rs/">Tokio</a>.</p><h2>Threads</h2><p>Rust offers threading support through the standard library. This example from the book can be copied directly into <a href="https://play.rust-lang.org/">Playground</a> and executed:</p><pre><code>use std::thread;
use std::time::Duration;

fn main() {
    thread::spawn(|| {
        for i in 1..10 {
            println!(&#8221;hi number {i} from the spawned thread!&#8221;);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!(&#8221;hi number {i} from the main thread!&#8221;);
        thread::sleep(Duration::from_millis(1));
    }
}</code></pre><h3>Closures</h3><p>The only thing new here is the strange argument to <code>spawn</code>, which we will review briefly.</p><p>Rust allows for anonymous functions that can capture the values of variables outside their scope at runtime. Python&#8217;s <code>lambda</code> is similar.</p><p>In the example above, the closure takes this form:</p><pre><code>|| {
    for i in 1..10 {
        println!(&#8221;hi number {i} from the spawned thread!&#8221;);
        thread::sleep(Duration::from_millis(1));
    }
}</code></pre><p>The <code>||</code> is where the inputs to the closure are placed. Since this closure doesn&#8217;t take any input, it is empty. The actions of the closure are defined just like any other function, including the curly braces.</p><p>If you wanted to be more explicit, you can assign the closure to a variable:</p><pre><code><code>let spawned_hi = || {
    for i in 1..10 {
        println!(&#8221;hi number {i} from the spawned thread!&#8221;);
        thread::sleep(Duration::from_millis(1));
    }
};</code></code></pre><p>Which you can call like a standard function::</p><pre><code><code>spawned_hi();</code></code></pre><p>Or pass it to the thread:</p><pre><code>thread::spawn(spawned_hi);</code></pre>
      <p>
          <a href="https://www.degencode.com/p/rust-for-pythonistas-part-iv-threading-and-concurrency">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Rust for Pythonistas]]></title><description><![CDATA[Part III: Error Handling & Type Conversions]]></description><link>https://www.degencode.com/p/rust-for-pythonistas-part-iii-error-handling-type-conversions</link><guid isPermaLink="false">https://www.degencode.com/p/rust-for-pythonistas-part-iii-error-handling-type-conversions</guid><dc:creator><![CDATA[BowTiedDevil]]></dc:creator><pubDate>Thu, 25 Sep 2025 15:01:56 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5c5ec501-2955-44a8-bad3-6b38460350a8_460x307.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Errors &amp; Exceptions</h2><p>Python uses exceptions to signal that something has gone wrong. A good Python app will anticipate what exceptions might be raised by various function or method calls and define the behavior when those exceptions occur.</p><p>Rust does not have exceptions. Instead it follows the &#8220;error as value&#8221; approach, where instead of raising an exception and breaking the normal control flow of the program, it will return an error instead of a value. The responsibility for handling both options falls to the calling code. Rust operations that can fail typically return a <code>Result</code> type which can hold one of two things: the value or an error.</p><p><a href="https://www.degencode.com/p/rust-for-pythonistas-part-ii-data-behavior-ownership-traits-and-alloy-tickmath">Part II</a> included Rust code with expressions ending with <code>.unwrap()</code> but I did not elaborate on why. Let&#8217;s get into it!</p><p><code>Result</code> is a type of Rust struct called an <code>enum</code>, which organizes and labels related values. It is defined in <a href="https://doc.rust-lang.org/std/result/enum.Result.html">std::result</a>:</p><pre><code>pub enum Result&lt;T, E&gt; {
    Ok(T),
    Err(E),
}</code></pre><p>Angled brackets make another appearance here, which signifies that <code>Result</code> is generic over types <code>T</code> and <code>E</code>. The enum holds two variants (<code>Ok</code>, and <code>Err</code>) which are named variants that wrap a value of a generic type.</p><p>Whenever you call a function that might fail, you should expect to receive a <code>Result</code>. The <code>Result</code> will either be an <code>Ok</code> wrapping the good value, or an <code>Err</code> wrapping the error value.</p><p>If you control the function and the input and know it will not fail, you might choose to skip the error checking and just access the value directly. Calling <code>unwrap()</code> on an <code>Ok</code> will extract the value from that wrapper. This is what I was doing in Part II.</p><p>Put this into <a href="https://play.rust-lang.org/">Rust Playground</a>:</p><pre><code>pub fn get_69() -&gt; Result&lt;u8, String&gt; {
    Ok(69)
}

fn main() {
    let known_good_value = get_69().unwrap();
    println!("{:?}", known_good_value);
}</code></pre><p>And run it to see:</p><pre><code>69</code></pre><p>Now let&#8217;s change the function to give an error instead:</p><pre><code>pub fn get_69() -&gt; Result&lt;u8, String&gt; {
    Err(String::from("Can't get 69, not nice"))
}</code></pre><p>Running this blows up with a panic message:</p><pre><code>thread 'main' panicked at src/main.rs:6:37:
called `Result::unwrap()` on an `Err` value: "Can't get 69, not nice"</code></pre><p>The <code>unwrap</code> method quickly peels off the wrapper holding a successful value. However if you <code>unwrap</code> an <code>Err</code>, the runtime will panic, triggering an immediate cleanup and shutdown.</p><p>Python is like this, too. If a program raises an exception which is not caught, it will eventually reach the entry point of the program and the interpreter will clean up and shut down.</p><h3>Matching</h3><p>Dealing with Rust errors is similar to using Python&#8217;s structural pattern matching (see <a href="https://peps.python.org/pep-064/">PEP-064</a>), whereby you can match directly on the value inside the wrapping variant:</p><pre><code>pub fn get_69() -&gt; Result&lt;u8, String&gt; {
    Ok(69)
    // Err(String::from("Can't get 69, not nice"))
}

fn main() {
    let unknown_value = get_69();
    match unknown_value {
        Ok(good_value) =&gt; println!("Got a value: {}", good_value),
        Err(message) =&gt; println!("Got an error: {}", message),
    }
}</code></pre><p>Running this, we get:</p><pre><code><code>Got a value: 69</code></code></pre><p>And after changing the function to return the <code>Err</code> instead:</p><pre><code>Got an error: Can't get 69, not nice</code></pre><p>The <code>match</code> keyword allows us to set control flow depending on the wrapper variant, and to assign the wrapped value to a variable that we could use. This allows us to offload the &#8220;did the call succeed?&#8221; flow to the match block, and concentrate on the &#8220;then do this&#8230;&#8221; logic in each arm that follows the match.</p><h3>The ? Operator</h3><p>Sometimes an error is unrecoverable, so there&#8217;s no point handling it. For situations where defining a complete <code>match</code> block would be overkill, we can use the <code>?</code> operator which will extract the wrapped values for either the <code>Ok</code> or the <code>Err</code> variant without using a full <code>match</code> block. </p><p>Try it out:</p><pre><code>pub fn get_69() -&gt; Result&lt;u8, String&gt; {
    // Ok(69)
    Err(String::from("Can't get 69, not nice"))
}

fn main() {
    let only_good_value = get_69()?;
}</code></pre><p>This fails because the <code>?</code> operator can cause <code>main</code> to return a <code>String</code> instead of nothing, which is the default return type. We can adjust the function signature to specify the two possibilities expressed as a <code>Result</code>:</p><pre><code>pub fn get_69() -&gt; Result&lt;u8, String&gt; {
    // Ok(69)
    Err(String::from("Can't get 69, not nice"))
}

fn main() -&gt; Result&lt;(), String&gt; {
    get_69()?;
    Ok(())
}</code></pre><p>The <code>Ok</code> variant runs successfully with no output, and the <code>Err</code> variant results in the message:</p><pre><code>Error: "Can't get 69, not nice"</code></pre><p>Notably, using the <code>?</code> operator on an <code>Err</code> variant does not trigger a panic like <code>unwrap</code> would &#8212; it just returns the wrapped error.</p><h2>Type Conversions</h2><p>The built-in Rust types are simple to convert. Like types can be converted in-line using <code>cast</code> in an assignment expression: </p><pre><code>fn main() {
    let i: u64 = 420;
    let j: u32 = i as u32;
    
    println!(&#8221;{i}&#8221;);
    println!(&#8221;{j}&#8221;);
}</code></pre><p>Output:</p><pre><code>420
420</code></pre><p>However, please be careful! Casting between different bit length types can result in wrapping if the source value exceeds the range of the destination type:</p><pre><code>fn main() {
    let i: u64 = 420;
    let j: i8 = i as i8;
    
    println!(&#8221;{i}&#8221;);
    println!(&#8221;{j}&#8221;);
}</code></pre><p>Here, the <code>i8</code> (8 bit signed integer) wraps from 127 to -128 when it reaches the upper limit of its range:</p><pre><code>420
-92</code></pre><h3>Complex Conversions</h3><p>As you move beyond the built-ins, you may want to perform more complex type conversions.</p>
      <p>
          <a href="https://www.degencode.com/p/rust-for-pythonistas-part-iii-error-handling-type-conversions">
              Read more
          </a>
      </p>
   ]]></content:encoded></item></channel></rss>