<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Jared Borders</title>
        <link>https://paragraph.com/@jaredborders</link>
        <description>undefined</description>
        <lastBuildDate>Mon, 31 Aug 2026 17:20:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Jared Borders</title>
            <url>https://storage.googleapis.com/papyrus_images/5aba9069831ea51ef30088a0f21f24406056a30fefb0d180e5e309e2cd7500a7.jpg</url>
            <link>https://paragraph.com/@jaredborders</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Perps: Quanto > Traditional?]]></title>
            <link>https://paragraph.com/@jaredborders/perps-quanto-traditional</link>
            <guid>D5apRpVGTPVDIM2xjsoW</guid>
            <pubDate>Wed, 20 Mar 2024 20:20:58 GMT</pubDate>
            <description><![CDATA[Quanto Perpetual Futures vs Traditional Perpetual Futures: An Overview of What, Why, and How + Exploring the mechanics, purpose, and distinctions.WhatBoth derivatives enable traders to speculate on an index asset by posting some collateral asset to secure the position. However, the numeraire asset used for settling payouts varies between markets. Quanto Perps uniquely allow traders&apos; profits and losses (PnL) to be settled in a numeraire asset that differs from the quote asset (which is us...]]></description>
            <content:encoded><![CDATA[<p>Quanto Perpetual Futures vs Traditional Perpetual Futures: An Overview of What, Why, and How + Exploring the mechanics, purpose, and distinctions.</p><h3 id="h-what" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What</h3><p>Both derivatives enable traders to speculate on an <strong>index asset</strong> by posting some <strong>collateral asset</strong> to secure the position. However, the <strong>numeraire asset</strong> used for settling payouts varies between markets.</p><p>Quanto Perps uniquely allow traders&apos; profits and losses (PnL) to be settled in a numeraire asset that differs from the <strong>quote asset</strong> (which is used to express or price the index asset). Typically, this quote asset for any index asset is <code>USD</code> in both Traditional and Quanto Perps.</p><h3 id="h-why" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Why</h3><p>While trading Traditional Perps, traders face exchange rate risk if the quote asset used to price the index asset <em>weakens</em>. Furthermore, even multi-collateral supported Traditional Perps do not provide full protection against exchange rate risk.</p><p>For example, if a position in a Traditional Perps market still settles PnL in a quote asset numeraire, weakening of the quote asset <strong>affects the index-derived PnL return</strong>.</p><p>🛠️ Quanto Perps fix this problem and more…</p><h3 id="h-how" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">How</h3><p>The central innovation of the <strong>quantity-adjusting</strong> (“Quanto”) derivative is its ability to adjust for changes in the value of the quote asset. If the quote asset weakens, then the derivative <strong><em>adjusts</em></strong> the <strong><em>quantity</em></strong> of the numeraire asset that can be exchanged for the quote asset to match the numeraire-to-quote exchange rate <strong>established at the position&apos;s onset</strong>.</p><p>This adjustment mechanism preserves PnL in terms of the numeraire, regardless of fluctuations in the exchange rate between the numeraire and quote asset during the position&apos;s lifetime.</p><h4 id="h-bonus" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">🎯 Bonus</h4><p>Establishing a fixed numeraire-to-quote exchange rate can have a “degen-welcomed” side effect when both the numeraire and index assets increase in value. <strong>Greater profits</strong> are achieved when PnL is calculated using the numeraire at a favorable exchange rate, as opposed to holding a similar position in Traditional Perpetual Contracts.</p><h3 id="h-quanto-continued-but-set-to-easy-difficulty" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Quanto Continued… (but set to easy difficulty)</h3><p>The content so far might have been a tough read if you are new to Perps, Quanto, Derivatives, DeFi, etc. The following work seeks to help break down the “What, Why, and How” further by stepping through two detailed trade lifecycle examples, explaining the basic math behind the payoffs, and emphasizing the significance of units (see <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/jaredborders.eth/9JWZAGP8o8plKXAZWY6h0shiw5y4lgmbHjqep3IXN4I">User-Defined Types in DeFi and Why You Should Care</a>).</p><hr><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/312bab454cf303948ff9aa417e7f1bbaeaa358e7b6174965eef1a65cb023a7b9.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>jaredborders@newsletter.paragraph.com (Jared Borders)</author>
        </item>
        <item>
            <title><![CDATA[User-Defined Types in DeFi and Why You Should Care]]></title>
            <link>https://paragraph.com/@jaredborders/user-defined-types-in-defi-and-why-you-should-care</link>
            <guid>VgaM0idsGea6Lk4DeKJZ</guid>
            <pubDate>Fri, 01 Mar 2024 23:57:47 GMT</pubDate>
            <description><![CDATA[As decentralized finance (DeFi) has matured, standardization has emerged as a critical factor in its evolution, enhancing and safeguarding interoperability—a feature widely regarded as one of the most unique and appealing aspects of DeFi. A prime example of this is the ERC-20 token standard, which has become the backbone of programmable money, defining how market participants can interact with digital currencies at both high and low levels. Standards like ERC-20 benefit all actors in the ecos...]]></description>
            <content:encoded><![CDATA[<p>As decentralized finance (DeFi) has matured, standardization has emerged as a critical factor in its evolution, enhancing and safeguarding interoperability—a feature widely regarded as one of the most unique and appealing aspects of DeFi. A prime example of this is the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum.org/developers/docs/standards/tokens/erc-20">ERC-20 token standard</a>, which has become the backbone of programmable money, defining how market participants can interact with digital currencies at both high and low levels. Standards like ERC-20 benefit all actors in the ecosystem, undeniably making DeFi a more robust and accessible field.</p><p>However, as the DeFi space evolves, so does the complexity of its protocols. Standardized building blocks, while foundational, cannot address the nuanced needs of increasingly sophisticated financial instruments.</p><h3 id="h-the-complexity-beyond-standardization" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>The Complexity Beyond Standardization</strong></h3><p>Some evolutions in DeFi cannot be realistically standardized due to the protocol specificity required by their code. When universal standards fall short, protocols must develop their own internal components, representing the unique financial instruments they aim to create. Therefore, these components require detailed scrutiny and validation through testing, auditing, formal verification, and other methodologies to meet the rigorous quality demands necessary for the safe handling of user funds.</p><h3 id="h-the-problem-of-comparing-apples-to-oranges" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>The Problem of Comparing Apples to Oranges</strong></h3><p>Most well-known protocols excel at creating and testing new components, yet there&apos;s still potential for enhancement, particularly regarding data types.</p><p>Take, for example, a <strong>perpetual futures market</strong>. It&apos;s a complex instrument, often simplified in code to the detriment of clarity and safety. This simplification results in a loss of critical information about the instrument&apos;s dimensions, such as the base asset unit, the asset unit for profit and loss (PnL) settlement, and the funding rate mechanism to balance market demand (just to name a few). These elements are crucial for understanding and managing the instrument but are frequently represented by overly simplified data types, like <code>uint256</code>, leading to significant information loss.</p><p>The nuances of financial instruments like the perpetual futures market are usually grossly under-defined within the code. This shortcoming forces developers to be the sole enforcers of proper arithmetic and logic between components, making it challenging for future developers, readers, or auditors to understand or test the logic as intended by the original developer. Without explicit information defining an instrument&apos;s dimensions, proper testing and validation can become fraught with mistakes.</p><blockquote><p>🍋 Imagine you&apos;re running a lemonade stand and decide to add fruit punch to your menu. A supplier offers to send you a batch of fruits, but you don&apos;t know the difference between lemons and strawberries. Assuming all fruits are as sour as lemons, you end up making your punch with only strawberries, expecting it to taste just right. But strawberries aren&apos;t as sour, and your punch is too sweet, disappointing customers who were excited for a tangy treat. This mix-up costs you sales and leaves you with a bunch of unsold, overly sweet punch. Similarly, in DeFi, if you mix up <strong>financial units</strong> without understanding their specific properties—like treating all tokens as if they have the same precision or transferability—you could make a costly mistake. It&apos;s like assuming all fruits can make good lemonade when not knowing the unique qualities of each can lead to unexpected and often unwanted results.</p></blockquote><h3 id="h-user-defined-value-types-a-solution" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>User-Defined Value Types: A Solution</strong></h3><p>User-defined value types in Solidity offer an alternative to representing dimensioned information, allowing for zero-cost abstractions of elementary data types (1). These types, combined with libraries that define and enforce interactions with the underlying type, offer a robust way to ensure that complex financial instruments are represented accurately and safely. This approach significantly benefits developers and auditors by making the code more intuitive and easier to reason about, compared to the use of basic data types for representing detailed financial instruments.</p><h3 id="h-trade-offs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Trade-Offs</strong></h3><h4 id="h-pros" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Pros</h4><p>Adopting a user-defined type system in smart contract development offers numerous and substantial benefits, laying the groundwork for code that is more secure, reliable, and transparent in its intentions.</p><p>At the core, user-defined types ensure that a developer&apos;s intent is preserved and enforced throughout the lifecycle of the code, transcending the limitations of traditional documentation, which may become outdated as the code evolves (4). This enduring accuracy is pivotal in smart contract development, where the stakes of maintaining integrity and security are high. User-defined types act as a constant source of truth, resistant to the inevitable changes that occur within a codebase.</p><p>Moreover, user-defined types are instrumental in establishing and maintaining invariants—conditions that are designed to remain true throughout the execution of a contract. These invariants play a critical role in enhancing the security and robustness of smart contracts. As the complexity of these types increases, so does their ability to assert and safeguard more of these crucial invariants, offering a systematic way to capture and enforce the contract&apos;s intended behaviors and constraints (4).</p><p>One of the most compelling advantages of user-defined types is their role in guiding refactoring efforts (4). Refactoring, the process of restructuring existing code without changing its external behavior, is essential for maintaining code quality and adaptability. User-defined types provide a clear framework for these efforts by clearly delineating how different parts of the system interact with each other. When a developer modifies one part of the system, the type system helps identify which other parts may be affected, ensuring that changes are consistent and that the system&apos;s integrity is maintained. This guidance is invaluable, especially in complex systems where interdependencies may not be immediately apparent, facilitating a more deliberate and informed approach to modifying code.</p><h4 id="h-cons" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Cons</h4><p>Despite these advantages, the implementation of user-defined value types introduces challenges, including an increase in verbosity, larger contract sizes, a steeper learning curve for developers, and potentially slower development processes. However, these challenges can often be mitigated. For example, the issue of increased contract size can be addressed through strategic router-proxy architecture solutions like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://archive.devcon.org/resources/6/alice-in-proxyland.pdf">Alice in Proxyland</a>, which is used by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://synthetix.io/">Synthetix</a>. Additionally, Solidity&apos;s support for user-defined operators can help reduce verbosity, making the code more intuitive and easier to interact with.</p><p>While these hurdles are noteworthy, the trade-offs are generally considered worthwhile for the benefits provided by a robust type system, particularly in terms of enhanced reliability, security, and code maintainability.</p><h4 id="h-working-example-user-define-operators" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Working Example: User-Define Operators</h4><p>Below is a snippet of code demonstrating how operator overloading can be implemented to simplify interactions with user-defined types (2):</p><pre data-type="codeBlock" text="pragma solidity ^0.8.19;

type Int is int;
using {add as +} for Int global;
using {sub as -, sub} for Int global;

function add(Int a, Int b) pure returns (Int) {
    return Int.wrap(Int.unwrap(a) + Int.unwrap(b));
}

function sub(Int a, Int b) pure returns (Int) {
    return Int.wrap(Int.unwrap(a) - Int.unwrap(b));
}

function test(Int x, Int y) pure {
    x + y;
    // ERROR: Member &quot;add&quot; not found or not visible 
    // after argument-dependent lookup in Int.
    x.add(y);

    x - y;
    // OK -- &quot;sub&quot; was also attached in &quot;using for&quot;
    x.sub(y); 
}
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.19;</span>

<span class="hljs-keyword">type</span> Int <span class="hljs-keyword">is</span> <span class="hljs-keyword">int</span>;
<span class="hljs-keyword">using</span> {<span class="hljs-title">add</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-operator">+</span>} <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">Int</span> <span class="hljs-title"><span class="hljs-keyword">global</span></span>;
<span class="hljs-keyword">using</span> {<span class="hljs-title">sub</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-operator">-</span>, <span class="hljs-title">sub</span>} <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">Int</span> <span class="hljs-title"><span class="hljs-keyword">global</span></span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params">Int a, Int b</span>) <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">Int</span>) </span>{
    <span class="hljs-keyword">return</span> Int.<span class="hljs-built_in">wrap</span>(Int.<span class="hljs-built_in">unwrap</span>(a) <span class="hljs-operator">+</span> Int.<span class="hljs-built_in">unwrap</span>(b));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sub</span>(<span class="hljs-params">Int a, Int b</span>) <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">Int</span>) </span>{
    <span class="hljs-keyword">return</span> Int.<span class="hljs-built_in">wrap</span>(Int.<span class="hljs-built_in">unwrap</span>(a) <span class="hljs-operator">-</span> Int.<span class="hljs-built_in">unwrap</span>(b));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params">Int x, Int y</span>) <span class="hljs-title"><span class="hljs-keyword">pure</span></span> </span>{
    x <span class="hljs-operator">+</span> y;
    <span class="hljs-comment">// ERROR: Member "add" not found or not visible </span>
    <span class="hljs-comment">// after argument-dependent lookup in Int.</span>
    x.add(y);

    x <span class="hljs-operator">-</span> y;
    <span class="hljs-comment">// OK -- "sub" was also attached in "using for"</span>
    x.sub(y); 
}
</code></pre><h3 id="h-real-world-application-kwentas-quanto-perpetual-futures-market" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Real-World Application: Kwenta&apos;s Quanto Perpetual Futures Market</strong></h3><p>At <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.kwenta.io/">Kwenta</a>, we&apos;re developing a new quantity-adjusting (“Quanto”) perpetual futures derivative, inspired by and based on a fork of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ttps://github.com/Synthetixio/synthetix-v3/tree/main/markets/perps-market">Synthetix v3 perpetual futures markets</a>. This market introduces a quanto asset to settle PnL, a significant innovation that was engineered simply by reimagining some of the underlying market units to achieve the desired payoff for our traders (3). Implementing and integrating a robust user-defined type system (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Kwenta/quanto-dimensions">quanto dimensions</a>) into our new market has allowed us to enforce all logic and arithmetic involving dimensioned components accurately, significantly enhancing the predictability and security of our product.</p><blockquote><p>See the full spec for quanto perpetual futures <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://gov.kwenta.eth.limo/kips/kip-117/">here</a></p></blockquote><h3 id="h-inspiration-from-jane-street" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Inspiration from Jane Street</strong></h3><p>The decision to leverage user-defined value types and to enhance the robustness of our financial instruments was heavily inspired by the advantages of a strong type system found in OCaml, as highlighted by Jane Street (4). The insights gained from exploring the benefits of types in programming have been crucial in shaping our approach to developing secure and reliable DeFi protocols, ensuring that we process significant sums of money responsibly.</p><h3 id="h-references" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">References:</h3><ol><li><p>User-Defined Value Types: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/v0.8.24/types.html#user-defined-value-types">Solidity Documentation</a> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://chat.openai.com/c/075c2469-e733-4845-b2e4-1918e49b57a3#user-content-fnref-1">↩</a></p></li><li><p>Feature Deep-Dive - User-Defined Operators: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://soliditylang.org/blog/2023/02/22/user-defined-operators/">Solidity Blog</a> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://chat.openai.com/c/075c2469-e733-4845-b2e4-1918e49b57a3#user-content-fnref-2">↩</a></p></li><li><p>Quanto Units: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/tommyrharper/shared-notes/blob/main/quanto/quantounits.pdf">GitHub Repository</a> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://chat.openai.com/c/075c2469-e733-4845-b2e4-1918e49b57a3#user-content-fnref-3">↩</a></p></li><li><p>Jane Street - Types, and Why You Should Care: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/watch?v=0arFPIQatCU">YouTube Video</a> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://chat.openai.com/c/075c2469-e733-4845-b2e4-1918e49b57a3#user-content-fnref-4">↩</a></p></li></ol>]]></content:encoded>
            <author>jaredborders@newsletter.paragraph.com (Jared Borders)</author>
        </item>
        <item>
            <title><![CDATA[Mastering EIP-712: Hashing Complex Data]]></title>
            <link>https://paragraph.com/@jaredborders/mastering-eip-712-hashing-complex-data</link>
            <guid>KKHcs6Try6uWOnWniNBu</guid>
            <pubDate>Fri, 15 Dec 2023 21:24:55 GMT</pubDate>
            <description><![CDATA[In this article, we will explore the correct methods for hashing and signing a complex data structure. This structure encompasses a combination of simple data such as bool, address, and uint256, as well as more intricate data like arrays and nested structs. Specifically, we&apos;ll focus on a structure utilized by Kwenta, a derivatives trading platform powered by Synthetix. This structure is pivotal in defining off-chain limit orders, which are subsequently executed on-chain, ensuring cryptog...]]></description>
            <content:encoded><![CDATA[<p>In this article, we will explore the correct methods for hashing and signing a complex data structure. This structure encompasses a combination of simple data such as <code>bool</code>, <code>address</code>, and <code>uint256</code>, as well as more intricate data like arrays and nested structs. Specifically, we&apos;ll focus on a structure utilized by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://kwenta.io/">Kwenta</a>, a derivatives trading platform powered by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://synthetix.io/">Synthetix</a>. This structure is pivotal in defining off-chain limit orders, which are subsequently executed on-chain, ensuring cryptographic security. The aim is to provide a clear and comprehensive guide, blending detailed, informative content with a friendly, blog-style approach.</p><blockquote><p>🌊 Before diving into the main content, it&apos;s expected that you already have some experience with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-712">EIP-712</a>. Additionally, possessing some familiarity with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/develop/abi-spec.html">encoding in Solidity</a> will be helpful.</p></blockquote><h2 id="h-1-background-on-limit-orders" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1. Background on Limit Orders</h2><p>I needed to build limit orders for Kwenta’s trading engine. These orders had to outline all aspects of a trade that could potentially be executed by an unknown party in the future. It was also vital to incorporate measures to protect the trade from exploiting the trader or the protocol when executed. Finally, they needed to be efficient, ensuring minimal on-chain computation.</p><p>A naive approach might involve storing the orders on-chain as soon as they are created, and then referring to these details later to ensure certain conditions are met before execution. However, this method can be expensive (for both the trader and protocol), especially if the conditions to be verified and stored on-chain are extensive.</p><h3 id="h-11-solution-off-chain-limit-orders" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.1 Solution: Off-Chain Limit Orders</h3><p>I discovered that signing structured data off-chain, with on-chain verification as described in EIP-712, offered an excellent solution to my challenge. This approach eliminates the need to store order details on-chain, significantly reducing gas consumption. Consequently, the level of specificity required for conditions when creating an order doesn&apos;t negatively impact traders, even if the order may never be executed. Additionally, canceling outstanding orders becomes more cost-effective because most of the work occurs off-chain, and the nonce (an on-chain identifier for orders used to mitigate replay attacks) can easily be invalidated on-chain as necessary. Furthermore, if a trader prefers to prioritize execution efficiency over absolute trustlessness, condition validation, which can be the most expensive aspect, can also be delegated to the off-chain system. However, it&apos;s important to note that certain details, such as nonce and signer authenticity, are always verified on-chain.</p><blockquote><p>📖 The off-chain verification piece of the solution is a bit out of scope for this article. If you do want to read about it, though, I have written extensively about the techniques/strategies I&apos;ve used in the protocols&apos; <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Kwenta/smart-margin-v3/wiki/Conditional-Orders">wiki</a>.</p></blockquote><h2 id="h-2-eip-712" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2. EIP-712</h2><p>Several articles do a fantastic job of walking through the process of hashing structured data following the standards described in EIP-712. However, I have found that many of these do not discuss some of the finer details related to encoding motivations nor how to handle more <em>complicated</em> structured data.</p><p>Let&apos;s start by defining the limit order data structure, which we will refer to as a <em>conditional order</em> for the rest of this article (as it is also referred to in documentation and code):</p><pre data-type="codeBlock" text="struct OrderDetails {
  uint128 marketId;
  uint128 accountId;
  int128 sizeDelta;
  uint128 settlementStrategyId;
  uint256 acceptablePrice;
  bool isReduceOnly;
  bytes32 trackingCode;
  address referrer;
}

struct ConditionalOrder {
  OrderDetails orderDetails;
  address signer;
  uint256 nonce;
  bool requireVerified;
  address trustedExecutor;
  uint256 maxExecutorFee;
  bytes[] conditions;
} 
"><code>struct OrderDetails {
  uint128 marketId<span class="hljs-comment">;</span>
  uint128 accountId<span class="hljs-comment">;</span>
  int128 sizeDelta<span class="hljs-comment">;</span>
  uint128 settlementStrategyId<span class="hljs-comment">;</span>
  uint256 acceptablePrice<span class="hljs-comment">;</span>
  bool isReduceOnly<span class="hljs-comment">;</span>
  bytes32 trackingCode<span class="hljs-comment">;</span>
  address referrer<span class="hljs-comment">;</span>
}

struct ConditionalOrder {
  OrderDetails orderDetails<span class="hljs-comment">;</span>
  address signer<span class="hljs-comment">;</span>
  uint256 nonce<span class="hljs-comment">;</span>
  bool requireVerified<span class="hljs-comment">;</span>
  address trustedExecutor<span class="hljs-comment">;</span>
  uint256 maxExecutorFee<span class="hljs-comment">;</span>
  bytes<span class="hljs-section">[]</span> conditions<span class="hljs-comment">;</span>
} 
</code></pre><blockquote><p>📖 The details around what specific member variables like <code>requireVerified</code> or <code>trackingCode</code> mean are unimportant here, but if you&apos;re interested, check out the wiki!</p></blockquote><p>To properly hash this data, you will want to define a couple of things first.</p><h3 id="h-21-domain-separator" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.1 Domain Separator</h3><p>The domain separator (<code>DOMAIN_SEPARATOR</code>) is a unique and contextually-based piece of data that serves a vital function by including domain-specific information to establish a foundational security layer. For example, the inclusion of <code>chainId</code> prevents a signed message from being executed on a duplicate contract that exists on a different chain. For our example, the domain separator will be the hash of the following encoded contents concatenated together:</p><ol><li><p>domain type hash (<code>DOMAIN_TYPEHASH</code>)</p></li><li><p>domain details (i.e., hashed name, hashed version, non-hashed chain id, non-hashed verifying contract)</p></li></ol><blockquote><p>🧂 An optional salt value may be appended to the domain details as a final measure, though its inclusion is not necessary in this instance.</p></blockquote><blockquote><p>📚 EIP-712 only mandates one domain details field, with additional fields being elective. This gives implementors flexibility in enhancing domain security (i.e., a <code>name</code> alone suffices to meet the standard&apos;s requirements).</p></blockquote><p>That&apos;s a lot of hashing already 😅 so here is some code to help illustrate:</p><pre data-type="codeBlock" text="bytes32 DOMAIN_TYPEHASH = keccak256(
  &quot;EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)&quot;
);

bytes32 DOMAIN_SEPARATOR = keccak256(
  abi.encode(
    DOMAIN_TYPEHASH,
    keccak256(name),
    keccak256(version),
    chainId,
    verifyingContract
  )
);
"><code><span class="hljs-keyword">bytes32</span> DOMAIN_TYPEHASH <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
  <span class="hljs-string">"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"</span>
);

<span class="hljs-keyword">bytes32</span> DOMAIN_SEPARATOR <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
  <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(
    DOMAIN_TYPEHASH,
    <span class="hljs-built_in">keccak256</span>(name),
    <span class="hljs-built_in">keccak256</span>(version),
    chainId,
    verifyingContract
  )
);
</code></pre><blockquote><p>🏌️‍♂️ The <code>DOMAIN_TYPEHASH</code> and <code>DOMAIN_SEPARATOR</code> are <em>typically</em> constant within a specific context, so they can be cached to save gas in subsequent transactions. See Solady’s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Vectorized/solady/blob/main/src/utils/EIP712.sol">_cachedDomainSeparator</a> for example.</p></blockquote><p>You may notice some “inconsistencies” already. Notice that some data, like the name and version, are hashed within the domain details, but others are not. When you are encoding any structured data following the EIP-712 specification, there are a few things to consider:</p><ol><li><p>Is the data an <strong>atomic type</strong>?</p></li><li><p>Is the data a <strong>dynamic type</strong>?</p></li><li><p>Is the data a <strong>reference type</strong>?</p></li></ol><blockquote><p>📚 EIP-712 neatly categorizes every type in Solidity into one of three distinct types, offering clear guidance on handling them. It&apos;s crucial to note, though, that EIP-712 is tailored to be EVM-specific but language-agnostic. This means it can be applied even in programming languages like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.vyperlang.org/en/stable/">Vyper</a>. However, as our focus is on Solidity, this article will be specifically curated to discuss concepts in Solidity terms.</p></blockquote><p>After determining the type of data you&apos;re working with, encoding it essentially becomes a process of following specific directions. However, take caution; correctly classifying the data type can be trickier than it seems. For instance, you might initially think of <code>address[]</code> as a dynamic type, but under EIP-712 guidelines, it&apos;s actually classified as a reference type.</p><h4 id="h-211-encoding-data-types" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">2.1.1 Encoding Data Types</h4><p><strong>Atomic</strong> data is straightforward. From the perspective of a Solidity developer, there&apos;s no need for additional preparation before encoding it.</p><blockquote><p>💡 Atomicity in Data Types: In some contexts, <em>atomic</em> refers to the simplest types of data that can&apos;t be broken down further. For example, an integer or a boolean value in a programming language is often considered atomic because it represents a single, indivisible value.</p></blockquote><p><strong>Dynamic</strong> data, such as <code>string</code> and <code>bytes</code>, simply requires hashing, which converts it into an atomic type (<code>bytes32</code>) before it is encoded. In our example, this applies to the member variables’ <code>name</code> and <code>version</code>.</p><p><strong>Reference</strong> types (<code>struct</code> and arrays defined as <code>T[]</code> where <code>T</code> is some generic type) are broken down into their contents which are then encoded recursively.</p><blockquote><p>📚 The standard defines reference types as “<em>(…) arrays and structs. Arrays are either fixed size or dynamic and denoted by</em> <code>Type[n]</code> <em>or</em> <code>Type[]</code><em>, respectively. Structs are references to other structs by their name.</em>”</p></blockquote><p>For encoding a <code>struct</code>, each member variable is processed based on its type. Take, for example, the <code>EIP712Domain</code> type; each of its member variables is encoded according to its specific type. <code>chainId</code> is an atomic type, so it can be encoded directly without any changes. However, <code>name</code> is a dynamic type, necessitating hashing before encoding. After encoding each member variable, the next step is to concatenate them in order, ensuring each encoded member is precisely 32 bytes in length. This might require padding for some member values, making the use of <code>abi.encode</code> essential (see later sections for why this matters). The need to hash dynamic types arises in part from this requirement for uniform 32-byte encodings.</p><p>For arrays, the encoding process involves processing each member (or element) individually based on its type, similar to how a <code>struct</code> is processed. These processed elements are then concatenated, adhering to the same rule of ensuring each encoded member is exactly 32 bytes in length, as established for encoding a <code>struct</code>.</p><blockquote><p>🚨 Despite the fact that <code>strings</code>/<code>bytes</code> are implemented as arrays “under the hood” (i.e., <code>Byte[]</code>, which technically follows the <code>T[]</code> pattern that defines reference arrays), the EIP clearly distinguishes them as &quot;dynamic types,&quot; while categorizing other arrays as &quot;reference types.&quot;</p></blockquote><h4 id="h-212-encode-vs-encodepacked" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">2.1.2 encode vs encodePacked</h4><p>Now is an opportune moment to pause and examine the distinct differences between <code>abi.encode</code> and <code>abi.encodePacked</code>. The use of <code>abi.encode</code> ensures unambiguous results as it includes metadata like type information and offsets. On the other hand, <code>abi.encodePacked</code> might yield ambiguous results, particularly when encoding two or more dynamic elements, because it excludes such metadata. <code>abi.encodePacked</code> creates a more compact encoding by skipping padding between elements, <strong>with the exception of arrays</strong>, where padding is indeed included. However, even with arrays, it&apos;s important to note that while <code>abi.encodePacked</code> may retain padding, it forfeits metadata. This distinction can be crucial in certain encoding scenarios.</p><p>Here&apos;s an illustration in code of how <code>abi.encodePacked</code> can result in ambiguous outcomes, and, in this specific scenario, lead to a collision:</p><pre data-type="codeBlock" text="abi.encodePacked(&quot;ab&quot;, &quot;c&quot;) == abi.encodePacked(&quot;a&quot;, &quot;bc&quot;)
"><code><span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-string">"ab"</span>, <span class="hljs-string">"c"</span>) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-string">"a"</span>, <span class="hljs-string">"bc"</span>)
</code></pre><p>In the previous example, it&apos;s worth noting that without including metadata, distinguishing between the two pieces of original data becomes impossible. However, including metadata alone may still not be sufficient. If we were to include metadata and combine everything without proper padding, it would remain exceedingly challenging to comprehend the data. Let&apos;s use the following example to illustrate this point:</p><pre data-type="codeBlock" text="console.logBytes(abi.encodePacked(&quot;ab&quot;, &quot;c&quot;));
/* 
0x
616263 
*/

console.logBytes(abi.encodePacked(&quot;a&quot;, &quot;bc&quot;));
/* 
0x
616263 
*/

console.logBytes(abi.encode(&quot;ab&quot;, &quot;c&quot;));
/* 
0x
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000002
6162000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
6300000000000000000000000000000000000000000000000000000000000000
*/

console.logBytes(abi.encode(&quot;a&quot;, &quot;bc&quot;));
/* 
0x
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000001
6100000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000002
6263000000000000000000000000000000000000000000000000000000000000
*/
"><code>console.logBytes(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-string">"ab"</span>, <span class="hljs-string">"c"</span>));
<span class="hljs-comment">/* 
0x
616263 
*/</span>

console.logBytes(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-string">"a"</span>, <span class="hljs-string">"bc"</span>));
<span class="hljs-comment">/* 
0x
616263 
*/</span>

console.logBytes(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(<span class="hljs-string">"ab"</span>, <span class="hljs-string">"c"</span>));
<span class="hljs-comment">/* 
0x
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000002
6162000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
6300000000000000000000000000000000000000000000000000000000000000
*/</span>

console.logBytes(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(<span class="hljs-string">"a"</span>, <span class="hljs-string">"bc"</span>));
<span class="hljs-comment">/* 
0x
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000001
6100000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000002
6263000000000000000000000000000000000000000000000000000000000000
*/</span>
</code></pre><p>If we removed padding from the latter two examples, we would have:</p><pre data-type="codeBlock" text="console.logBytes(abi.encode(&quot;ab&quot;, &quot;c&quot;)) -&gt; 0x408026162163
console.logBytes(abi.encode(&quot;a&quot;, &quot;bc&quot;)) -&gt; 0x408016126263
"><code>console.logBytes(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(<span class="hljs-string">"ab"</span>, <span class="hljs-string">"c"</span>)) <span class="hljs-operator">-</span><span class="hljs-operator">></span> <span class="hljs-number">0x408026162163</span>
console.logBytes(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(<span class="hljs-string">"a"</span>, <span class="hljs-string">"bc"</span>)) <span class="hljs-operator">-</span><span class="hljs-operator">></span> <span class="hljs-number">0x408016126263</span>
</code></pre><p>The integrity of this data without padding has been compromised because it is no longer evident where one encoded piece of content or metadata starts or ends (just like we saw in <code>abi.encodePacked</code>). When data is padded to conform uniformly to 32-byte words, it becomes feasible to predictably parse the data word by word.</p><p>Here’s another example, using a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/forge/tests">test in Foundry</a>, that demonstrates how <code>abi.encodePacked</code> pads arrays but omits metadata, in contrast to <code>abi.encode</code>, which preserves such information:</p><pre data-type="codeBlock" text="function test_1() public pure {
  uint256[] memory arr = new uint256Unsupported embed;
  arr[0] = 6;
  arr[1] = 9;
  
  // see contents of encodedArr &amp; encodePackedArr below
  bytes memory encodedArr = abi.encode(arr);
  bytes memory encodePackedArr = abi.encodePacked(arr);

  assert(keccak256(encodedArr) != keccak256(encodePackedArr));
}

/*

encodedArr:
0x
0000000000000000000000000000000000000000000000000000000000000020 (offset)
0000000000000000000000000000000000000000000000000000000000000002 (length)
0000000000000000000000000000000000000000000000000000000000000006 (element)
0000000000000000000000000000000000000000000000000000000000000009 (element)

encodePackedArr:
0x
0000000000000000000000000000000000000000000000000000000000000006 (element)
0000000000000000000000000000000000000000000000000000000000000009 (element)

*/
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_1</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> </span>{
  <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> arr <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> uint256Unsupported embed;
  arr[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> <span class="hljs-number">6</span>;
  arr[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> <span class="hljs-number">9</span>;
  
  <span class="hljs-comment">// see contents of encodedArr &#x26; encodePackedArr below</span>
  <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> encodedArr <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(arr);
  <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> encodePackedArr <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(arr);

  <span class="hljs-built_in">assert</span>(<span class="hljs-built_in">keccak256</span>(encodedArr) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(encodePackedArr));
}

<span class="hljs-comment">/*

encodedArr:
0x
0000000000000000000000000000000000000000000000000000000000000020 (offset)
0000000000000000000000000000000000000000000000000000000000000002 (length)
0000000000000000000000000000000000000000000000000000000000000006 (element)
0000000000000000000000000000000000000000000000000000000000000009 (element)

encodePackedArr:
0x
0000000000000000000000000000000000000000000000000000000000000006 (element)
0000000000000000000000000000000000000000000000000000000000000009 (element)

*/</span>
</code></pre><p>Observe how both encodings apply padding to the array, but the packed version omits the metadata, specifically the offset and length.</p><blockquote><p>🎬 If you wish to learn all there is to know about ABI encoding for Solidity, check out this fantastic <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://youtu.be/upVloLUw5Z0?si=2sS9Rh7vAZtclX4e">video</a> dedicated to the topic.</p></blockquote><h3 id="h-22-typed-data" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.2 Typed Data</h3><p>We need to establish a hash that captures both the shape and values of the typed data. Looking at the conditional order data structure we defined earlier, it becomes evident that we&apos;re handling another reference type, similar to the <code>EIP712Domain</code>. To add to the complexity, one of the structs includes a member variable that represents a non-fixed length array of dynamically typed data (a reference type (<code>T[]</code>) of dynamic types (<code>T</code> is of type <code>bytes</code>)). What a mess 😵‍💫!</p><h4 id="h-221-order-hash" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">2.2.1 Order Hash</h4><p>Let&apos;s start with the simpler task of hashing the nested order details <code>struct</code> member variable of the conditional order <code>struct</code> and generating an <code>ORDER_HASH</code>. This object defines specific order details for a given perpetual futures market in Synthetix v3. Since it doesn&apos;t contain any dynamic or reference types, the process should be pretty easy.</p><pre data-type="codeBlock" text="bytes32 ORDER_DETAILS_TYPEHASH = keccak256(
  &quot;OrderDetails(uint128 marketId,uint128 accountId,int128 sizeDelta,uint128 settlementStrategyId,uint256 acceptablePrice,bool isReduceOnly,bytes32 trackingCode,address referrer)&quot;
);

bytes32 ORDER_HASH = keccak256(
  abi.encode(
    ORDER_DETAILS_TYPEHASH,
    marketId,
    accountId,
    sizeDelta,
    settlementStrategyId,
    acceptablePrice,
    isReduceOnly,
    trackingCode,
    referrer
  )
);
"><code><span class="hljs-keyword">bytes32</span> ORDER_DETAILS_TYPEHASH <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
  <span class="hljs-string">"OrderDetails(uint128 marketId,uint128 accountId,int128 sizeDelta,uint128 settlementStrategyId,uint256 acceptablePrice,bool isReduceOnly,bytes32 trackingCode,address referrer)"</span>
);

<span class="hljs-keyword">bytes32</span> ORDER_HASH <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
  <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(
    ORDER_DETAILS_TYPEHASH,
    marketId,
    accountId,
    sizeDelta,
    settlementStrategyId,
    acceptablePrice,
    isReduceOnly,
    trackingCode,
    referrer
  )
);
</code></pre><p>Consider how we use <code>abi.encode</code> and not <code>abi.encodePacked</code>. We want this order hash to be predictive and non-ambiguous for the same reasons listed previously. Using <code>abi.encodePacked</code> here would pack any data <em>less than 32 bytes</em> together without padding. Another interesting observation is that <code>abi.encode,</code> in this case, does not include <em>any</em> metadata because all of the contents are atomic.</p><p>Observe the following example, showing that even without metadata, all the content is comprehensively included (also, notice the differences between both encoding schemes):</p><pre data-type="codeBlock" text="function test_2() public pure {
  address addr = address(0xBEEF);
  bytes32 b1 = bytes32(uint256(99));
  uint256 num1 = 19;
  uint128 num2 = 29;
  
  bytes memory encode = abi.encode(addr, b1, num1, num2);
  bytes memory encodePacked = abi.encodePacked(addr, b1, num1, num2);

  assert(keccak256(encode) != keccak256(encodePacked));
}

/*

encode:
0x
000000000000000000000000000000000000000000000000000000000000beef
0000000000000000000000000000000000000000000000000000000000000063
0000000000000000000000000000000000000000000000000000000000000013
000000000000000000000000000000000000000000000000000000000000001d

encodePacked:
0x
000000000000000000000000000000000000beef000000000000000000000000
0000000000000000000000000000000000000063000000000000000000000000
0000000000000000000000000000000000000013000000000000000000000000
0000001d

*/
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_2</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> </span>{
  <span class="hljs-keyword">address</span> addr <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0xBEEF</span>);
  <span class="hljs-keyword">bytes32</span> b1 <span class="hljs-operator">=</span> <span class="hljs-keyword">bytes32</span>(<span class="hljs-keyword">uint256</span>(<span class="hljs-number">99</span>));
  <span class="hljs-keyword">uint256</span> num1 <span class="hljs-operator">=</span> <span class="hljs-number">19</span>;
  <span class="hljs-keyword">uint128</span> num2 <span class="hljs-operator">=</span> <span class="hljs-number">29</span>;
  
  <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> encode <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(addr, b1, num1, num2);
  <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> encodePacked <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(addr, b1, num1, num2);

  <span class="hljs-built_in">assert</span>(<span class="hljs-built_in">keccak256</span>(encode) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(encodePacked));
}

<span class="hljs-comment">/*

encode:
0x
000000000000000000000000000000000000000000000000000000000000beef
0000000000000000000000000000000000000000000000000000000000000063
0000000000000000000000000000000000000000000000000000000000000013
000000000000000000000000000000000000000000000000000000000000001d

encodePacked:
0x
000000000000000000000000000000000000beef000000000000000000000000
0000000000000000000000000000000000000063000000000000000000000000
0000000000000000000000000000000000000013000000000000000000000000
0000001d

*/</span>
</code></pre><h4 id="h-222-conditional-order-hash" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">2.2.2 Conditional Order Hash</h4><p>Next, we tackle the conditional order hash (<code>CONDITIONAL_ORDER_HASH</code>), the most complex data structure we&apos;ve faced so far. By carefully navigating each step, I aim to highlight and address any remaining questions about handling dynamic and reference types.</p><pre data-type="codeBlock" text="bytes32 CONDITIONAL_ORDER_TYPEHASH = keccak256(
  &quot;ConditionalOrder(OrderDetails orderDetails,address signer,uint256 nonce,bool requireVerified,address trustedExecutor,uint256 maxExecutorFee,bytes[] conditions)OrderDetails(uint128 marketId,uint128 accountId,int128 sizeDelta,uint128 settlementStrategyId,uint256 acceptablePrice,bool isReduceOnly,bytes32 trackingCode,address referrer)&quot;
);

bytes32 CONDITIONAL_ORDER_HASH = keccak256(
  abi.encode(
    CONDITIONAL_ORDER_TYPEHASH,
    ORDER_HASH,
    signer,
    nonce,
    requireVerified,
    trustedExecutor,
    maxExecutorFee,
    conditions /***** TODO: this will NOT work 🚨 *****/
  )
);
"><code><span class="hljs-keyword">bytes32</span> CONDITIONAL_ORDER_TYPEHASH <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
  <span class="hljs-string">"ConditionalOrder(OrderDetails orderDetails,address signer,uint256 nonce,bool requireVerified,address trustedExecutor,uint256 maxExecutorFee,bytes[] conditions)OrderDetails(uint128 marketId,uint128 accountId,int128 sizeDelta,uint128 settlementStrategyId,uint256 acceptablePrice,bool isReduceOnly,bytes32 trackingCode,address referrer)"</span>
);

<span class="hljs-keyword">bytes32</span> CONDITIONAL_ORDER_HASH <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
  <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(
    CONDITIONAL_ORDER_TYPEHASH,
    ORDER_HASH,
    signer,
    nonce,
    requireVerified,
    trustedExecutor,
    maxExecutorFee,
    conditions <span class="hljs-comment">/***** <span class="hljs-doctag">TODO:</span> this will NOT work 🚨 *****/</span>
  )
);
</code></pre><blockquote><p>📚 Notice how the order details member variable (<code>orderDetails</code> in the <code>CONDITIONAL_ORDER_TYPEHASH</code>) is treated relative to the other types within the type hash. Don’t be confused! From the standard: “<em>If the struct type </em><strong><em>references other struct types</em></strong><em> (…), then the set of referenced struct types is collected, sorted by name, and appended to the encoding.</em>”</p></blockquote><p>I intentionally marked the <code>conditions</code> member variable as “<code>TODO</code>” to emphasize how we handle this particular type of data. Since the <code>conditions</code> member variable is a reference type (i.e., it is of type <code>bytes[]</code>), it&apos;s essential to encode it following precise instructions to ensure that the result is predictable and free from collisions.</p><p>So, would this work?</p><pre data-type="codeBlock" text="bytes32 hashedConditions = keccak256(conditions);
"><code>bytes32 <span class="hljs-attr">hashedConditions</span> = keccak256(conditions)<span class="hljs-comment">;</span>
</code></pre><p>As you might’ve guessed, it would not.</p><p>You may wonder, &quot;Well, a string is an array of bytes, and we got a <em>predictive and non-ambiguous result</em> from hashing the whole thing. So why can&apos;t we do the same with the <code>conditions</code> member variable?&quot;</p><p>It&apos;s crucial to keep in mind that the EIP provides precise guidelines on how to handle dynamic types. So, why do we handle dynamic types differently than explicit array types? The reason lies in the versatility of arrays, as they can contain elements of any type, including dynamic and reference types. Consequently, if we don&apos;t encode the members of an array correctly, collisions can occur, as demonstrated in the example given earlier (in 2.1.2). These collisions could lead to unpredictable and unsafe data, underscoring the importance of handling dynamic types with care.</p><p>Now that we&apos;ve established the need to encode each element in an array, let&apos;s apply these principles to our <code>conditions</code>. Each element in this array is a dynamic type, which means we need to hash each element prior to encoding. Simple enough. Once each element has been hashed, we can proceed to concatenate them. However, it&apos;s important to note that this can be a potential point of confusion. The EIP specifies that we <em>only need to concatenate each </em><strong><em>encoded element</em></strong>.</p><blockquote><p>🚨 In this context, our objective is to avoid including any additional data, specifically metadata related to the array being encoded.</p></blockquote><p>Examining <code>test_1()</code> from section 2.1.2, when we encode the array <code>arr</code> using <code>abi.encode</code>, we observe that it indeed pads each element in the array. However, it also prefixes the data with metadata. Given our specification for array handling, we aim to <strong>exclude</strong> this metadata from the encoding. In contrast, <code>abi.encodePacked</code> pads each element in the array sequentially, and importantly, it <strong>does not include metadata</strong>. The latter aligns perfectly with our requirements, making it the preferred choice for our encoding needs.</p><p>So, below is one way you can encode the <code>conditions</code> member variable:</p><pre data-type="codeBlock" text="bytes32[] memory hashedConditionElements;

for (uint256 i = 0; i &lt; conditions.length; i++) {
  hashedConditionElements[i] = keccak256(conditions[i]);
}

bytes32 hashedConditions =
  keccak256(abi.encodePacked(hashedConditionElements));
"><code><span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">memory</span> hashedConditionElements;

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> conditions.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
  hashedConditionElements[i] <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(conditions[i]);
}

<span class="hljs-keyword">bytes32</span> hashedConditions <span class="hljs-operator">=</span>
  <span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(hashedConditionElements));
</code></pre><p>Based on our knowledge of encoding reference types, consider if we knew the array of conditions only contained only two elements. With that information, we could actually assert the following:</p><pre data-type="codeBlock" text="// assume conditions variable is defined elsewhere with type bytes[]
function test_3() public pure {
  bytes32 hash1 = keccak256(
    abi.encode(
      keccak256(conditions[0]),
      keccak256(conditions[1]),
    )
  );

  bytes32 hash2 = keccak256(
    abi.encodePacked(
      keccak256(conditions[0]),
      keccak256(conditions[1]),
    )
  );
  
  assert(hash1 == hash2);
}
"><code><span class="hljs-comment">// assume conditions variable is defined elsewhere with type bytes[]</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_3</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> </span>{
  <span class="hljs-keyword">bytes32</span> hash1 <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
    <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(
      <span class="hljs-built_in">keccak256</span>(conditions[<span class="hljs-number">0</span>]),
      <span class="hljs-built_in">keccak256</span>(conditions[<span class="hljs-number">1</span>]),
    )
  );

  <span class="hljs-keyword">bytes32</span> hash2 <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
    <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(
      <span class="hljs-built_in">keccak256</span>(conditions[<span class="hljs-number">0</span>]),
      <span class="hljs-built_in">keccak256</span>(conditions[<span class="hljs-number">1</span>]),
    )
  );
  
  <span class="hljs-built_in">assert</span>(hash1 <span class="hljs-operator">=</span><span class="hljs-operator">=</span> hash2);
}
</code></pre><p>In the contrived example above, the choice between using <code>abi.encode</code> or <code>abi.encodePacked</code> <strong>doesn&apos;t actually matter</strong> in terms of the result. In both cases, we are encoding two atomic types, each with a length of 32 bytes, and there&apos;s no metadata involved. Therefore, <code>abi.encode</code> doesn&apos;t need to add any padding, and <code>abi.encodePacked</code> wouldn&apos;t have included padding regardless. The result is consistent in terms of encoding these specific atomic types.</p><p>Here&apos;s an even simpler <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/forge/fuzz-testing">fuzz test</a> showcasing the logic:</p><pre data-type="codeBlock" text="function test_4(bytes32 x, bytes32 y) public pure {
  bytes memory encode = abi.encode(x, y);
  bytes memory encodePacked = abi.encodePacked(x, y); 

  assert(keccak256(encode) == keccak256(encodePacked));
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_4</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> x, <span class="hljs-keyword">bytes32</span> y</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> </span>{
  <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> encode <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(x, y);
  <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> encodePacked <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(x, y); 

  <span class="hljs-built_in">assert</span>(<span class="hljs-built_in">keccak256</span>(encode) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(encodePacked));
}
</code></pre><p>However, in the wild, when the length of the conditions is unknown, we need to iterate over each element, hash it, and then create a new array (<code>hashedConditionElements</code>). Afterward, <strong>we must use</strong> <code>abi.encodePacked</code> to concatenate every <em>processed</em> element; otherwise, metadata will be included when it shouldn’t be. Only in cases when the array length is known could we write code similar to <code>test_3()</code>.</p><blockquote><p>🧠 Make sure you understand when to use <code>abi.encodePacked</code>. If you neglect to use <code>abi.encodePacked</code> when attempting to exclude metadata, your on-chain signature verification system could yield false-negative results when widely-used Web3 libraries like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ethers.org/v6/">ethers</a> or <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://viem.sh/">viem</a> generate correct signatures based on EIP-712.</p></blockquote><p>Below is an example asserting that encoding an array via <code>abi.encodePacked</code> and <code>abi.encode</code> will yield different results:</p><pre data-type="codeBlock" text="function test_5(bytes32 x, bytes32 y) public pure {
  bytes32[] memory arr = new bytes32Unsupported embed;
  arr[0] = x;
  arr[1] = y;

  assert(keccak256(abi.encodePacked(arr)) != keccak256(abi.encode(arr)));
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_5</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> x, <span class="hljs-keyword">bytes32</span> y</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> </span>{
  <span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">memory</span> arr <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> bytes32Unsupported embed;
  arr[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> x;
  arr[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> y;

  <span class="hljs-built_in">assert</span>(<span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(arr)) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(arr)));
}
</code></pre><p>Now that we know how to process <code>conditions</code>, let&apos;s build the conditional order hash again.</p><h4 id="h-223-bring-it-all-together" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">2.2.3 Bring It All Together</h4><pre data-type="codeBlock" text="CONDITIONAL_ORDER_TYPEHASH = keccak256(
  &quot;ConditionalOrder(OrderDetails orderDetails,address signer,uint256 nonce,bool requireVerified,address trustedExecutor,uint256 maxExecutorFee,bytes[] conditions)OrderDetails(uint128 marketId,uint128 accountId,int128 sizeDelta,uint128 settlementStrategyId,uint256 acceptablePrice,bool isReduceOnly,bytes32 trackingCode,address referrer)&quot;
);

bytes32[] memory hashedConditionElements;

for (uint256 i = 0; i &lt; co.conditions.length; i++) {
  hashedConditionElements[i] = keccak256(co.conditions[i]);
}

bytes32 hashedConditions = 
  keccak256(abi.encodePacked(hashedConditionElements));

CONDITIONAL_ORDER_HASH = keccak256(
  abi.encode(
    CONDITIONAL_ORDER_TYPEHASH,
    ORDER_HASH,
    signer,
    nonce,
    requireVerified,
    trustedExecutor,
    maxExecutorFee,
    hashedConditions /***** this will work ✅ *****/
  )
);
"><code>CONDITIONAL_ORDER_TYPEHASH <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
  <span class="hljs-string">"ConditionalOrder(OrderDetails orderDetails,address signer,uint256 nonce,bool requireVerified,address trustedExecutor,uint256 maxExecutorFee,bytes[] conditions)OrderDetails(uint128 marketId,uint128 accountId,int128 sizeDelta,uint128 settlementStrategyId,uint256 acceptablePrice,bool isReduceOnly,bytes32 trackingCode,address referrer)"</span>
);

<span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">memory</span> hashedConditionElements;

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> co.conditions.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
  hashedConditionElements[i] <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(co.conditions[i]);
}

<span class="hljs-keyword">bytes32</span> hashedConditions <span class="hljs-operator">=</span> 
  <span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(hashedConditionElements));

CONDITIONAL_ORDER_HASH <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
  <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(
    CONDITIONAL_ORDER_TYPEHASH,
    ORDER_HASH,
    signer,
    nonce,
    requireVerified,
    trustedExecutor,
    maxExecutorFee,
    hashedConditions <span class="hljs-comment">/***** this will work ✅ *****/</span>
  )
);
</code></pre><p>Now that we&apos;ve successfully hashed the conditional order, let&apos;s document some key observations that can serve as valuable principles for future reference:</p><ol><li><p>Any data type that requires declaration of its storage location (e.g., <code>memory</code>, <code>storage</code>) needs preprocessing before encoding.</p></li><li><p>Reference types are processed recursively, meaning their nested components are also encoded following the same principles.</p></li><li><p>While <code>abi.encode</code> consistently ensures data predictability and safety, it&apos;s worth noting that in certain cases, <code>abi.encodePacked</code> can achieve the same outcome and must be used.</p></li></ol><h2 id="h-3-final-destination" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3. Final Destination</h2><p>To arrive at the final destination, which is the final hash, we must hash everything that has been created up to this stage:</p><pre data-type="codeBlock" text="bytes32 msgHash = keccak256(
  abi.encodePacked(
    &quot;\x19\x01&quot;,
    domainSeparator,
    CONDITIONAL_ORDER_HASH
  )
);
"><code><span class="hljs-keyword">bytes32</span> msgHash <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
  <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(
    <span class="hljs-string">"\x19\x01"</span>,
    domainSeparator,
    CONDITIONAL_ORDER_HASH
  )
);
</code></pre><p>Once more, we observe that we can confidently utilize <code>abi.encodePacked</code> while still adhering to the standard. The contents being encoded in this context, thanks to the precise rules followed in generating the <code>CONDITIONAL_ORDER_HASH</code> and <code>domainSeparator</code>, are <strong>deterministic</strong>. Additionally, there is only one dynamic type present (the prefix string), and we deliberately avoid padding it. Consequently, concerns about collisions are unwarranted in this scenario.</p><h3 id="h-31-signing-the-message-bonus" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.1 Signing the Message (Bonus)</h3><p>As a final step, Foundry lets us (in Solidity 🙏) define a private key, generate a public key from it, and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/cheatcodes/sign">sign data</a> in a test environment. Below is how you can sign the hash we created:</p><pre data-type="codeBlock" text="(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, msgHash);
"><code>(<span class="hljs-keyword">uint8</span> v, <span class="hljs-keyword">bytes32</span> r, <span class="hljs-keyword">bytes32</span> s) <span class="hljs-operator">=</span> vm.sign(privateKey, msgHash);
</code></pre><p>Kwenta utilizes a different approach for signature generation in its front end. Similar to many Web3 applications, it depends on a third party tool (not written in Solidity) for creating signatures. To guarantee that our on-chain verification mechanism correctly authenticates signatures produced by this tool, we have incorporated a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Kwenta/smart-margin-v3/blob/main/test/Signature.test.ts">hardhat test</a> to assert the anticipated result in this context. I strongly recommend that anyone working with EIP-712 verify their work by testing its functionality against battle-tested third-party tools such as ethers and viem.</p><hr><p>That&apos;s it 🏁</p><p>Thank you for reading, and I hope that this information proves valuable in addressing any challenges you may encounter while hashing complex typed data. If you have any questions, require clarification, or have differing perspectives on any of the points presented, please feel free to reach out!</p><p>Special thanks to the following colleagues for their contributions to this content, whether through discussions, peer reviews, or guidance: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/tommyrharper">Tom</a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/jcmonte">Jeremy</a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/insulineru">Aleksey</a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/avclarke">Adam</a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/koredefashokun">Korede</a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Melvillian">Melville</a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/kingofclubstroyDev">Jordan</a>, and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/jesperkristensen58">Jesper</a>.</p><hr><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/JaredBorders">https://github.com/JaredBorders</a></p>]]></content:encoded>
            <author>jaredborders@newsletter.paragraph.com (Jared Borders)</author>
        </item>
    </channel>
</rss>