<?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>Albert.Lin</title>
        <link>https://paragraph.com/@albertlin</link>
        <description>https://twitter.com/ksin751119</description>
        <lastBuildDate>Sun, 05 Apr 2026 23:56:08 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Albert.Lin</title>
            <url>https://storage.googleapis.com/papyrus_images/87714dc64a41b72cfe828f55f9262f0d5bb0db43cda1b95e1be916d9703256e5.jpg</url>
            <link>https://paragraph.com/@albertlin</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Exploring the Core Mechanism of UniswapV4]]></title>
            <link>https://paragraph.com/@albertlin/exploring-the-core-mechanism-of-uniswapv4</link>
            <guid>CxD2bREp53mEMxJBgXbK</guid>
            <pubDate>Sun, 08 Oct 2023 13:54:39 GMT</pubDate>
            <description><![CDATA[Since the announcement of UniswapV4, this swapping platform has undergone a significant transformation, evolving from a simple swapping platform into an infrastructure service provider. Especially noteworthy is the Hooks feature in V4, which has garnered widespread attention. After conducting in-depth research over a period of time, I have compiled some content to help everyone better understand this transformation and its implementation. The innovation in UniswapV4 doesn&apos;t focus on impr...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/bd437f0cf5daeec9679b7862ad27fe0847811f7879b3c1c5b191b542c7c6daee.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><p>Since the announcement of UniswapV4, this swapping platform has undergone a significant transformation, evolving from a simple swapping platform into an infrastructure service provider. Especially noteworthy is the Hooks feature in V4, which has garnered widespread attention. After conducting in-depth research over a period of time, I have compiled some content to help everyone better understand this transformation and its implementation.</p><p>The innovation in UniswapV4 doesn&apos;t focus on improving AMM technology per se but rather on expanding the ecosystem. Specifically, this innovation includes several key features:</p><ol><li><p>Flash Accounting</p></li><li><p>Singleton Contract</p></li><li><p>Hooks Architecture</p></li></ol><p>In the following sections, I will provide a detailed explanation of the significance of these features and how they are implemented.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/354dca3859a127093a33edcc036f9fa3f762ff26a2452e721c162ec4ba3002a9.png" alt="source: https://twitter.com/jermywkh/status/1670779830621851650" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">source: https://twitter.com/jermywkh/status/1670779830621851650</figcaption></figure><hr><h1 id="h-flash-accounting" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Flash Accounting</strong></h1><h3 id="h-double-entry-bookkeeping" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Double Entry Bookkeeping</strong></h3><p>UniswapV4 adopts a recording method similar to Double Entry Bookkeeping to track the changes in token balances corresponding to each operation. This double-entry bookkeeping recording method requires that each transaction must be recorded in multiple accounts simultaneously, ensuring that the asset values between these accounts remain balanced. For example, suppose a user exchanges 100 TokenA for 50 TokenB in the pool. The ledger would record it as follows:</p><ul><li><p>USER: TokenA decreases by 100 units (-100), and TokenB increases by 50 units (+50).</p></li><li><p>POOL: TokenA increases by 100 units (+100), and TokenB decreases by 50 units (-50).</p></li></ul><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9776a25dd6bae51760cdf65127c08baf3880e36d478913524636b1a6a7694bd9.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><h3 id="h-operations-related-to-token-delta" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Operations Related to Token Delta</h3><p>In UniswapV4, the primary operations use this accounting method and employ a storage variable called <code>lockState.currencyDelta[currency]</code> in the code to record the change in token balances. The numerical value of this change, if positive, represents the expected increase in the token within the pool, while if negative, it signifies the expected decrease in the token within the pool. From another perspective, a positive value indicates the shortage of tokens in the pool (the expected amount to be received), whereas a negative value indicates the surplus of tokens in the pool (the expected amount for users to withdraw). The following lists the impact of various operations on Token Delta:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/da3855226d2c6158d13d52d54eda025631c79eda53f0d20504a56564113dc203.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><p>Here is the translation of the descriptions of various operations on Token Delta in UniswapV4:</p><ul><li><p><strong>modifyPosition</strong>: Represents the execution of Add/Remove liquidity operations. For Add liquidity, it uses addition to update Token Delta (indicating the expected addition of TokenA to the pool). For Remove liquidity, subtraction is used to update Token Delta (indicating the expected withdrawal of TokenB from the pool).</p></li><li><p><strong>swap</strong>: Represents the execution of a Swap operation. Taking the example of swapping TokenA for TokenB, addition is used to update TokenADelta, while subtraction is used to update TokenBDelta.</p></li><li><p><strong>settle</strong>: Accompanies the transfer of tokens to the Pool. The Pool calculates the increase in tokens before and after the operation and uses subtraction to update Token Delta. If the Pool receives the exact expected amount of tokens, the subtraction here precisely sets TokenDelta to zero.</p></li><li><p><strong>take</strong>: Accompanies the withdrawal of tokens from the Pool. The Pool uses addition to update Token Delta, indicating that tokens have been removed from this Pool.</p></li><li><p><strong>mint</strong>: The behavior of updating Token Delta is similar to &quot;take,&quot; but minting does not actually withdraw tokens from the pool. Instead, it issues corresponding ERC1155 Tokens as proof of withdrawal, while the tokens remain in the pool. Later, users can retrieve tokens from the pool by burning the ERC1155 Tokens. There are two purposes for this: 1. To save gas costs associated with ERC20 token transfers (contract call + one less storage write). In the future, TokenDelta can be updated using the burn method of ERC1155 tokens for trading purposes. 2. To keep liquidity in the pool, maintaining liquidity depth for users to have a better Swap Token experience.</p></li><li><p><strong>donate</strong>: Indicates the intention to donate tokens to the Pool, but in practice, you still need to use &quot;settle&quot; to send the tokens into the Pool. Therefore, addition is used here to update Token Delta.</p></li></ul><p>The operations described above only involve actual token transfers in the cases of &quot;settle&quot; and &quot;take.&quot; For the other operations, their primary purpose is to update the TokenDelta values without physically transferring tokens.</p><h3 id="h-token-delta-example" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Token Delta Example</strong></h3><p>Let&apos;s illustrate how TokenDelta is updated with a simple example. Suppose today we exchange 100 TokenA for 50 TokenB:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/cd46992fe7fa189650a555d6ee31ae3d9b3b014a80b87924e919d71f3f551956.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><ol><li><p>Before the transaction starts, both TokenADelta and TokenBDelta are 0.</p></li><li><p>Swap: Calculate how much TokenA the Pool needs to receive and how much TokenB the user will receive. At this point, TokenADelta = 100, and TokenBDelta = -50.</p></li><li><p>Settle: Send 100 TokenA into the Pool and update TokenADelta = 100 - 100 = 0.</p></li><li><p>Take: Withdraw 50 TokenB from the Pool to the user&apos;s account, and update TokenBDelta = -50 + 50 = 0.</p></li></ol><p>Absolutely, when TokenADelta and TokenBDelta are both reset to 0 after the entire exchange operation is completed, it signifies that the operation is fully balanced, ensuring consistency in account balances. This meticulous tracking and resetting mechanism are essential for maintaining accurate and reliable accounting within the system.</p><h3 id="h-eip-1153-transient-storage-opcodes" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>EIP-1153: Transient storage opcodes</strong></h3><p>Mentioned that UniswapV4 uses Storage Variables to record TokenDelta, but within smart contracts, reading and writing to Storage Variables can be quite costly. This is where another EIP introduced by Uniswap comes into play: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/taipei-ethereum-meetup/exploring-the-core-mechanisms-of-uniswap-v4-83a491fdf6be">EIP1153 - Transient Storage Opcodes.</a></p><p>UniswapV4 plans to utilize the TSTORE and TLOAD opcodes provided by EIP1153 to update TokenDelta. Storage Variables using Transient Storage Opcodes will be discarded after the transaction ends (similar to Memory Variables), which means they do not need to be written to disk, thus reducing gas costs.</p><p>EIP1153 has been confirmed to be included in the upcoming <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.ethereumcatherders.com/dencun/">Dencun Upgrade</a>, and UniswapV4 has indicated that they will go live with this upgrade after Constantinople. This approach should help reduce the gas costs associated with these operations while still maintaining accurate accounting within the protocol.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/48d2d3c73373572ca75dc39428a75b211b8545d816b94b7bdf2d374228b7f7ab.png" alt="source: https://etherworld.co/2022/12/13/transient-storage-for-beginners/" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">source: https://etherworld.co/2022/12/13/transient-storage-for-beginners/</figcaption></figure><h3 id="h-flash-accounting-lock" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Flash Accounting — Lock</strong></h3><p>UniswapV4 has introduced a locking mechanism, which means that before performing any Pool operations, you must first call <code>PoolManager.lock()</code> to acquire a lock. Before the execution of <code>lock()</code> is complete, it checks whether the TokenDelta value is 0; otherwise, it will trigger a revert. After calling <code>PoolManager.lock()</code> and successfully acquiring the lock, the <code>lockAcquired()</code> function of <code>msg.sender</code> will be called. In the <code>lockAcquired()</code> function, the operations related to the Pool (such as swap, modifyPosition, etc.) are executed.</p><p>Let&apos;s illustrate this process with a diagram. When a user needs to perform a Token Swap operation, they must call a Smart Contract with a <code>lockAcquired()</code> function (referred to as the Callback Contract). The Callback Contract will first call <code>PoolManager.lock()</code>, and then <code>PoolManager</code> will call the <code>lockAcquired()</code> function of the Callback Contract. In the <code>lockAcquired()</code> function, the logic related to Pool operations, such as swap, settle, and take, is defined. Finally, just before the entire <code>lock()</code> is about to end, <code>PoolManager</code> checks whether the TokenDelta related to this operation has been reset to 0 to ensure that the assets in the Pool remain balanced.</p><p>This locking mechanism helps ensure the integrity and consistency of operations involving the Pool, preventing any discrepancies or unintended changes in token balances.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1fddd9a7fadc29eef38ee8d4f4fda88a38bc93ed2a38ebe3706190a0528e7666.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><hr><h1 id="h-singleton-contract" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Singleton Contract</strong></h1><p>The introduction of the Singleton Contract in UniswapV4 signifies the abandonment of the previous Factory-Pool model. Each Pool is no longer an independent Smart Contract; instead, all Pools share a single singleton contract. This design, combined with the Flash Accounting mechanism, only requires the updating of necessary Storage Variables, further reducing the complexity and cost of operations.</p><p>Let&apos;s illustrate this with a diagram. In the case of UniswapV3, converting ETH to DAI required at least four Token transfers (Storage write operations). This involved multiple changes recorded for USDC, USDT, and DAI Tokens. However, with the improvements in UniswapV4, coupled with the Flash Accounting mechanism, only one Token transfer is needed (transferring DAI from the Pool to the user), significantly reducing the number of operations and costs.</p><p>This streamlined approach simplifies transactions and makes them more cost-efficient, benefiting users of the UniswapV4 platform.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9949b98fd0ed856cbc105d0f08219ba3b75210526aceb756b667b9b6d1d9d097.png" alt="source: https://twitter.com/Uniswap/status/1671208668304486404" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">source: https://twitter.com/Uniswap/status/1671208668304486404</figcaption></figure><hr><h1 id="h-hooks-architecture" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Hooks Architecture</strong></h1><p>UniswapV4&apos;s most notable update in this release is the Hooks Architecture. This update provides significant flexibility around Pool usability. Hooks refer to additional actions called when specific operations are performed on the Pool. These actions are categorized into different classes, including initialize (create pool), modifyPosition (add/remove liquidity), swap, and donate, with each class having pre-execution and post-execution actions:</p><ul><li><p>beforeInitialize / afterInitialize</p></li><li><p>beforeModifyPosition / afterModifyPosition</p></li><li><p>beforeSwap / afterSwap</p></li><li><p>beforeDonate / afterDonate</p></li></ul><p>This design allows users to execute custom logic before and after specific operations, providing greater flexibility in extending the functionality of UniswapV4.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9de01982dcee64a74586cac237e184be48a85016501cf72f611e444bcbdfe97e.png" alt="source: https://github.com/Uniswap/v4-core/blob/main/whitepaper-v4-draft.pdf" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">source: https://github.com/Uniswap/v4-core/blob/main/whitepaper-v4-draft.pdf</figcaption></figure><h3 id="h-hook-example-limit-order-hook" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Hook Example — Limit Order Hook</strong></h3><p>Next, we will use an example of a Limit Order to explain the actual operational process of Hooks. Before we begin, let&apos;s briefly explain the principle of implementing Limit Orders in UniswapV4.</p><p>The principle behind implementing limit orders in UniswapV4 involves adding liquidity to a specific price range and then executing the removal of liquidity if that range of liquidity is traded.</p><p>For example, let&apos;s say we added liquidity in the price range of 1900–2000 for ETH, and then the price of ETH rises from 1800 to 2100. At this point, all the ETH liquidity we previously added in the 1900–2000 price range has been exchanged for USDC (assuming it&apos;s an ETH-USDC Pool). Removing liquidity at this moment is equivalent to executing a market order for ETH at the current price range of 1900–2000.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ed21b13f0972009ef8cd83a2afa3188fadc91e9a5bcab65127d0892ce1937819.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><h3 id="h-limit-order-hook-contract" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Limit Order Hook Contract</strong></h3><p>This example is provided by UniswapV4 on its <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Uniswap/v4-periphery/blob/main/contracts/hooks/examples/LimitOrder.sol">GitHub</a> platform. In this example, the Limit Order Hook contract offers two hooks, namely afterInitialize and afterSwap. The afterInitialize hook is used to record the price range (tick) when creating the Pool, allowing identification of which limit orders have been matched after someone performs a swap.</p><p><strong>Place Order</strong></p><p>When a user needs to place an order, the Hook contract executes the operation of adding liquidity based on the price range and quantity specified by the user. In the Limit Order Hook contract, you can observe the presence of the <code>place()</code> function. The primary logic involves calling the <code>lockAcquiredPlace()</code> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Uniswap/v4-periphery/blob/main/contracts/hooks/examples/LimitOrder.sol#L246">function</a> after acquiring the lock, which executes the operation of adding liquidity. This part is equivalent to placing a limit order.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6477abf1a47061f65e41d34f26daa6462b0841d29eb9052abe7a0792e27b36da.png" alt="source: https://github.com/Uniswap/v4-periphery/blob/main/contracts/hooks/examples/LimitOrder.sol#L246" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">source: https://github.com/Uniswap/v4-periphery/blob/main/contracts/hooks/examples/LimitOrder.sol#L246</figcaption></figure><p><strong>afterSwap Hook</strong></p><p>After a user completes a token swap within this Pool, the Pool calls the <code>afterSwap()</code> function of the Hook contract. The main logic behind <code>afterSwap()</code> is to remove liquidity for the previously executed order operations between the previous price range and the current price range. This behavior is equivalent to the order being filled, indicating that the order has been executed.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5bb72fd9d29e206b588e7ca9f82a99fc86f923aa16009a998c6238e4336ce427.png" alt="source: https://github.com/Uniswap/v4-periphery/blob/main/contracts/hooks/examples/LimitOrder.sol#L192" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">source: https://github.com/Uniswap/v4-periphery/blob/main/contracts/hooks/examples/LimitOrder.sol#L192</figcaption></figure><h3 id="h-limit-order-flow" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Limit Order Flow</strong></h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/895a00d0618a9e72cf35dadeee155991f876da3277c978ec9b37a8ed4d773c4f.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><p>The entire process of implementing a Limit Order using the Hook mechanism is as follows:</p><ol><li><p>The order placer sends the order to the Hook contract.</p></li><li><p>The Hook contract executes the liquidity addition operation based on the order information.</p></li><li><p>Regular users perform token swaps within the Pool.</p></li><li><p>After the token swap is completed, the Pool calls the <code>afterSwap()</code> function of the Hook contract.</p></li><li><p>The Hook contract, based on the price range changes from the token swap, executes the removal of liquidity for the executed limit orders.</p></li></ol><p>This sequence illustrates how the Hook mechanism is used to implement the entire process of Limit Orders within the UniswapV4 framework.</p><hr><h1 id="h-hook-other-features" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Hook: Other features</strong></h1><h3 id="h-hooks-contract-address-bit" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Hooks Contract Address Bit</strong></h3><p>Whether to execute the before/after specific operations is determined by the leftmost 1 byte of the Hook contract address. 1 byte consists of 8 bits, which precisely corresponds to 8 additional actions. The Pool checks whether the bit of that action is set to 1 to determine whether it should call the corresponding hook function in the Hook contract. This also means that Hook contract addresses need to be designed in a specific way and cannot be arbitrarily chosen as Hook contracts. This design primarily aims to reduce gas consumption by shifting the cost to contract deployment, thereby achieving more efficient operations. (PS: In practice, you can use different CREATE2 salts to calculate contract addresses that meet the criteria through brute force.)</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/07b887e5c1568a8ee64d55ed7138b91f84d77b731dbcd4b99c88efb517ad25ed.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><h3 id="h-dynamic-fee" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Dynamic Fee</strong></h3><p>In addition to allowing additional actions to be executed before and after each operation, Hooks also support the implementation of dynamic fees. When creating a Pool, you can specify whether to enable dynamic fees. If dynamic fees are enabled, the <code>getFee()</code> function of the Hook contract is called during token swaps. The Hook contract can then determine how much fee should be charged based on the current state of the Pool. This design allows for fee calculations to be adjusted based on real-time conditions, enhancing the flexibility of the system.</p><h3 id="h-pool-creation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Pool Creation</strong></h3><p>Each Pool needs to determine its Hook contract at the time of creation, and this cannot be changed afterward (though different Pools can share the same Hook contract). This is primarily because Hooks are considered part of the PoolKey, and PoolManager uses the PoolKey to identify which Pool to operate on. Even if the assets are the same, having different Hook contracts would classify them as different Pools. This design ensures that the states and operations of different Pools can be independently managed and maintains the consistency of each Pool.</p><p>However, this design can also lead to increased complexity in routing as the number of Pools grows. Solutions like UniswapX might be designed to address this issue.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/32c48bd664ee2c55970cb0850c6d44d8f87c8e93197cf5787d1f4de5ef1f93ad.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><hr><h2 id="h-tldr" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">TL;DR</h2><ul><li><p>Flash Accounting is used to track the quantity changes of each Token, ensuring that all changes are reset to zero after a transaction. To save on gas fees, Flash Accounting utilizes a special storage method provided by EIP1153.</p></li><li><p>The Singleton Contract design helps reduce gas consumption by avoiding updates to multiple storage variables.</p></li><li><p>The Hooks architecture provides additional operations divided into &quot;pre-execution&quot; and &quot;post-execution&quot; phases, making each Pool operation more flexible but also complicating Pool routing.</p></li></ul><p>UniswapV4 clearly emphasizes expanding the entire Uniswap ecosystem and transforming it into infrastructure for more services to be built on Uniswap Pools. This helps enhance Uniswap&apos;s competitiveness and reduces the risk of other services replacing it, but its success remains to be observed. Some highlights include the combination of Flash Accounting and EIP1153, which we believe will lead to more services adopting these features and various application scenarios emerging in the future. This is the core concept of UniswapV4, and we hope it provides a deeper understanding of how UniswapV4 operates. If there are any errors in the article, please feel free to correct them, and we welcome discussions and feedback.</p><hr><h2 id="h-reference" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reference</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/i/web/status/1668736365297799168">Robert Chen Tweet</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/i/web/status/1668666926095282176">DeFi Cheetah Tweet</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.panewslab.com/zh/articledetails/5lz5jp4c.html">深度解读Uniswap V4：一个“集大成者”的去中心化交易所</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.uniswap.org/uniswap-v4">Uniswap Lab Blog</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.bankless.com/what-you-need-to-know-about-uniswap-v4/">What You Need to Know About Uniswap v4</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/saucepoint/v4-template">v4-template</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/jtriley-eth/huff-hooks">Huff Hooks</a></p></li><li><br></li><li><br></li><li><br></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Uniswap/v4-core">UniswapV4 Github</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Uniswap/v4-core/blob/main/whitepaper-v4-draft.pdf">UniswapV4 WhitePaper</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.blocktempo.com/what-is-new-and-staying-the-same-of-uniswap-v4/">Introducing v4 with Hayden Adams and the Protocols Team</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.blocktempo.com/uniswap-v4-is-coming/">Uniswap V4 版本降臨！你該知道 V4 的全部升級重點</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.blocktempo.com/how-does-uniswap-v4-implement-limit-order-trading/">Uniswap V4 重點性能「Hooks」是如何實現限價單交易的？</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.blocktempo.com/uniswap-v4-can-dex-surpass-cex/">Uniswap V4 帶來的終極問題：DEX能超越CEX嗎？</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.panewslab.com/zh/articledetails/k1mygg95en8r.html">Uniswap进化史：马行千里，V4的带来机会和影响</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.theblockbeats.info/news/42643">关于Uniswap V4，你需要知道的一切</a></p></li></ul>]]></content:encoded>
            <author>albertlin@newsletter.paragraph.com (Albert.Lin)</author>
        </item>
        <item>
            <title><![CDATA[TornadoCash V2: Privacy-Pools and "Proof-of-Innocence"]]></title>
            <link>https://paragraph.com/@albertlin/tornadocash-v2-privacy-pools-and-proof-of-innocence</link>
            <guid>DNNTLSbiDBf69yLnLfEk</guid>
            <pubDate>Sat, 10 Jun 2023 03:35:10 GMT</pubDate>
            <description><![CDATA[IntroductionTornadoCash is the most renowned anonymous transaction service in the world of cryptocurrency. TornadoCash utilizes Zero-Knowledge Proof (ZKP) technology to conceal the source of funds. The mechanism behind TornadoCash was criticized by the United States government for facilitating illegal financial activities, leading to its enforced delisting by the U.S. Treasury Department in August 2022. Privacy protection and money laundering activities often seem closely intertwined in many ...]]></description>
            <content:encoded><![CDATA[<h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/TornadoCash">TornadoCash</a> is the most renowned anonymous transaction service in the world of cryptocurrency. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/TornadoCash">TornadoCash</a> utilizes Zero-Knowledge Proof (ZKP) technology to conceal the source of funds. The mechanism behind TornadoCash was criticized by the United States government for facilitating illegal financial activities, leading to its enforced delisting by the U.S. Treasury Department in August 2022. Privacy protection and money laundering activities often seem closely intertwined in many cases. While pursuing privacy, malicious actors often exploit these privacy features for illicit money laundering. Is it possible to find a method that allows people to maintain their privacy while effectively curbing money laundering activities? A possible direction has been suggested by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.privacypools.com/">Privacy-Pools</a>, developed by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/ameensol">ameen.eth</a>, one of the early developers of TornadoCash. (Regarding the delisting, only the frontend website and GitHub repository were affected, while the contract portion remains unaffected on the blockchain. Eventually, the GitHub repository was restored under the efforts of the Electronic Frontier Foundation. For more details, please refer to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.eff.org/deeplinks/2022/08/code-speech-and-tornado-cash-mixer">this source</a>.)</p><hr><h2 id="h-introduction-to-tornadocash-mechanism" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction to TornadoCash Mechanism</h2><p>Before introducing Privacy-Pools, it is important to understand the design principles behind TornadoCash. For a detailed explanation, you can refer to my previous article titled &quot;<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/albertlin.eth/9odRdkZHI0T_vY857Dnft1ZCpEMR4-RctNkfBDFH0c4">Breaking Down TornadoCash: A Beginner&apos;s Guide to Explaining its Functionality to Friends</a>.&quot; Here, I will briefly review the design principles of TornadoCash.</p><p>TornadoCash utilizes receipts, known as <code>commitments</code>, to control access rights. A commitment is generated by hashing a <code>secret</code> value and a <code>nullifier</code> code together. Each <code>commitment</code> can only be used for one withdrawal. Deposit information is recorded using a <code>Merkle Tree</code> structure, with commitments serving as leaf nodes, and the Merkle Root is calculated. Users only need to provide the data path from the leaf to the root to prove that the data is one of the leaves in the <code>Merkle Tree</code>, thereby indirectly proving the previous deposit of funds into TornadoCash. Zero-Knowledge Proofs are used to hide the source of the deposit, while <code>nullifiers</code> are used to prevent Double Withdrawal attacks.</p><p>TornadoCash&apos;s commitments serve two purposes:</p><ol><li><p><strong>Proving Previous Deposit</strong></p></li><li><p><strong>Ensuring Single Withdrawal</strong></p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/89c4eb14706b0f2868ea5445567bbb92ba47a31afbda8d51e221979038476bdb.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><hr><h2 id="h-proof-of-innocence" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">“Proof-of-Innocence”</h2><p>According to the design principles of TornadoCash, it is only possible to determine that the withdrawn funds come from a previous deposit, but the specific deposit cannot be identified. This anonymity feature is exploited by criminals for illicit money laundering activities, which is why the US government seeks to regulate TornadoCash. However, if we can provide an alternative proof, such as Zero-Knowledge Proof (ZKP), to demonstrate that the withdrawn funds do not originate from deposits on the blacklist, it could potentially prove that the withdrawal is not associated with illicit activities. This concept is known as &quot;Proof-of-Innocence,&quot; which aims to establish the innocence of the individual involved.</p><p>The concept of <strong>&quot;Proof-of-Innocence&quot;</strong> can be divided into two directions:</p><ol><li><p>Proving that the withdrawn funds originate from the set of deposits listed on the whitelist (Allowed List).</p></li><li><p>Proving that the withdrawn funds do not come from the set of deposits listed on the blacklist (Denied List).</p></li></ol><p>These two directions aim to establish the legitimacy of the withdrawn funds by either demonstrating their association with authorized deposits or their dissociation from prohibited deposits.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/37b5840191a1f72eaa663b3bfa117a462908207163d3631b13891a88fa61e47a.png" alt="source: https://medium.com/@chainway_xyz/introducing-proof-of-innocence-built-on-tornado-cash-7336d185cda6" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">source: https://medium.com/@chainway_xyz/introducing-proof-of-innocence-built-on-tornado-cash-7336d185cda6</figcaption></figure><hr><h2 id="h-the-design-principle-of-privacy-pools" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The design principle of Privacy-Pools</h2><p>The design principle of Privacy-Pools builds upon TornadoCash and adds the concept of <strong>&quot;Proof-of-Innocence.&quot;</strong> In Privacy-Pools, the withdrawal receipt serves not only its original purpose of representing a TornadoCash receipt but also carries a third meaning: <strong>&quot;proving that the withdrawn funds originate from deposits on the whitelist.&quot;</strong></p><p>By incorporating this additional proof, Privacy-Pools aims to provide an enhanced level of assurance regarding the legitimacy of the withdrawn funds, ensuring that they can be traced back to authorized deposits listed on the whitelist.</p><p>The receipt from Privacy-Pools represents the following meanings:</p><ol><li><p><strong>Proving Previous Deposit</strong></p></li><li><p><strong>Ensuring Single Withdrawal</strong></p></li><li><p><strong>Proof that the withdrawn funds originate from deposits on the whitelist</strong></p></li></ol><p>Here, we use the <strong>Allow Merkle Tree</strong> to explain how Privacy-Pools incorporates the concept of <strong>&quot;Proof-of-Innocence&quot;</strong> into the system (where the Allow Merkle Tree represents the whitelist concept). First, the Allow Merkle Tree has several characteristics.</p><ul><li><p>As the name suggests, the Allow Merkle Tree is a Merkle Tree.</p></li><li><p>The height of the tree and the number of nodes are the same as the Deposit Merkle Tree in Privacy-Pools.</p></li><li><p>Each leaf of the Allow Merkle Tree corresponds to the position of a leaf in the Deposit Merkle Tree (representing a deposit).</p></li></ul><p>The data in the leaf nodes of the Allow Merkle Tree can be categorized into the following two types:</p><ul><li><p><code>allowed</code>: This indicates that the deposit at that position is allowed. (Positions without any deposits are also considered allowed by default.)</p></li><li><p><code>blocked</code>: This indicates that the deposit at that position is blocked or denied.</p></li></ul><p>The image below illustrates that the positions at index <code>0</code> and <code>1</code> in the Deposit Merkle Tree represent legal funds, and their corresponding positions in the Allow Merkle Tree are also marked as <code>allowed</code>.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fb9e43a44823712c0d1f833db788e65078024f90b2455957be2f9c42659c2606.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><p>Let&apos;s assume that today a criminal wants to engage in money laundering activities and deposits illicit funds into Privacy-Pools at the position with Deposit Merkle Tree <code>ndex: 2</code>. We are aware that these funds are illicit, so we update the corresponding position in the Allow Merkle Tree, specifically <code>index: 2</code>, to <code>blocked</code> This action ensures that the blocked funds cannot be withdrawn as they are associated with illicit activities.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5925e4bb01fb093a1a15d933c3763a3f2f124980e4e09c18e6b24bb5bdcd8186.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><h2 id="h-in-the-case-of-withdrawals-from-the-allowed-list" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">In the case of withdrawals from the allowed list</h2><p>Suppose a user, whose deposit is listed on the US government&apos;s allowed list, wants to withdraw funds. In addition to providing proof of having a deposit in the Deposit Merkle Tree, they also need to provide proof of being <code>allowed</code> on the US allowed list. The corresponding proof of the US allowed list includes the Allow Merkle Root (provided by the user, referred to as <code>subsetRoot</code> in the code) and the intermediate node values encountered along the way.</p><p>During the ZKP verification stage in Privacy-Pools, the Merkle Root is constructed using the leaf value <code>allowed</code> (in the actual code, it is <code>keccak256(allowed)</code>) and the given intermediate node values. The system then checks whether this calculated Merkle Root matches the Allow Merkle Root provided by the user. If they match, the verification is successful, indicating that the withdrawn funds come from a deposit listed on the US government&apos;s allowed list.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e4f8293a2fae5dc3d5f39433c2a5201e9cc4622fd109a5ada1137e08e590a01a.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><h2 id="h-in-the-case-of-withdrawals-from-the-denied-list" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">In the case of withdrawals from the denied list</h2><p>If a user, whose deposit <strong>is not listed</strong> on the US government&apos;s allowed list, attempts to withdraw funds and their corresponding deposit position in the US allowed list is marked as <code>blocked</code> it would prevent the user from using the Allow Merkle Root associated with the US government&apos;s allowed list to withdraw funds. This would result in a verification failure as the corresponding proof cannot be generated.</p><p>In Privacy-Pools, the calculations rely on the leaf value <code>allowed</code> (<code>keccak256(allowed)</code> in the code), but the US government&apos;s allowed list marks that position as <code>blocked.</code> This discrepancy leads to the inability to calculate the same Merkle Root, resulting in a verification failure. As a result, the user would not be able to withdraw funds using the Allow Merkle Root from the US government&apos;s allowed list.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/37524108c5e6d89b2d9bfd40fc4c9702be1bf64727045ca0c8dea7a5c24eb11e.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><p>Under this design, the user would indeed need to provide an alternative Allow Merkle Root to withdraw funds. This alternative Allow Merkle Tree must mark the corresponding deposit position as <code>allowed</code> in order to calculate the same Allow Merkle Root for successful verification. This alternative Allow Merkle Tree can come from another government or institution that maintains such a list or even be generated by the user themselves.</p><p>In this scenario, the US government could use the Allow Merkle Root used during the withdrawal process to determine whether the user&apos;s funds comply with US government regulations, enabling them to track the funds. If the user is using a self-generated or untrusted Allow Merkle Tree, it is highly likely that the withdrawn funds come from problematic deposits, as any reputable third-party Allow Merkle Tree would mark that deposit position as <code>blocked.</code></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/59efaed4f32c63b81569b805487cc4bf7f75bb8f1207d6fb3232698a8285bb71.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><hr><h2 id="h-faq" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">FAQ:</h2><p><strong>Q: If the Allow Merkle Root is provided by the withdrawer, can they forge a fake Allow Merkle Root to claim that the leaf corresponds to an allowed deposit? Does this mean they can still withdraw the funds?</strong></p><p>A: The answer is affirmative. It is indeed possible to withdraw the funds in such a scenario. The author specifically points out that the intention of this mechanism is not to prevent criminals from withdrawing the funds, but rather to ensure that even if they succeed in doing so, it becomes known that the funds originated from the denied list. When the withdrawer provides an unconvincing Allow Merkle Root, it can be assumed that they are withdrawing funds from a deposit on the denied list. The reason for allowing this behavior is speculated to be to maintain the decentralized nature of the service. Each Allow Merkle Tree requires certain permission management to update the status of each leaf. If a specific Allow Merkle Tree root were enforced, it would mean that someone has certain privileges to control the withdrawal of funds, which goes against the spirit of decentralization.</p><p><strong>Q: Who decides whether this transaction comes from funds on the denied list?</strong></p><p>A: The author does not specifically mention who decides whether a transaction comes from funds on the denied list. It is understood that this aspect is likely to be determined by the respective regulatory authorities. For example, if the US government intends to investigate illicit funds in Privacy-Pools, they could examine each transaction&apos;s Allow Merkle Root to determine if it is associated with illicit funds. The criteria for determining which Allow Merkle Roots are considered valid or allowed would be determined by the respective regulatory authorities themselves.</p><hr><h2 id="h-privacy-pools-code" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Privacy-Pools Code</h2><p>Here, I have provided the main code along with the author&apos;s comments, hoping it will help everyone understand the main logic through the code.</p><pre data-type="codeBlock" text="// circuits/withdraw_from_subset.circom
template WithdrawFromSubset(levels, expectedValue) {
    // public
    signal input root;
    signal input subsetRoot;
    signal input nullifier;
    signal input assetMetadata; // abi.encode(token, amount).snarkHash();
    signal input withdrawMetadata; // abi.encode(recipient, refund, relayer, fee).snarkHash();

    // private
    signal input secret; 
    signal input path; // Indicate whether the data represents the left leaf or the right leaf.
    signal input mainProof[levels];  // Construct the data required for deposit root.
    signal input subsetProof[levels]; // Construct the data required for allow root.

    // Calculate the nullifier and commitment.
    component hasher = CommitmentNullifierHasher();
    hasher.secret &lt;== secret;
    hasher.path &lt;== path;
    hasher.assetMetadata &lt;== assetMetadata;
    nullifier === hasher.nullifier;

    // expectedValue: keccak256(&quot;allowed&quot;) % p
    component doubleTree = DoubleMerkleProof(levels, expectedValue);
    doubleTree.leaf &lt;== hasher.commitment;
  
    // Convert the path to bits to specify whether it is the left leaf or the right leaf. 
    // It can be observed that the deposit tree and allow tree share the same path.
    doubleTree.path &lt;== path; 
    for (var i = 0; i &lt; levels; i++) {
        doubleTree.mainProof[i] &lt;== mainProof[i];
        doubleTree.subsetProof[i] &lt;== subsetProof[i];
    }
    root === doubleTree.root; // Verify the deposit root.
    subsetRoot === doubleTree.subsetRoot; // Verify the allow root.

    signal withdrawMetadataSquare;
    withdrawMetadataSquare &lt;== withdrawMetadata * withdrawMetadata;
}
"><code><span class="hljs-comment">// circuits/withdraw_from_subset.circom</span>
template WithdrawFromSubset(levels, expectedValue) {
    <span class="hljs-comment">// public</span>
    signal input root;
    signal input subsetRoot;
    signal input nullifier;
    signal input assetMetadata; <span class="hljs-comment">// abi.encode(token, amount).snarkHash();</span>
    signal input withdrawMetadata; <span class="hljs-comment">// abi.encode(recipient, refund, relayer, fee).snarkHash();</span>

    <span class="hljs-comment">// private</span>
    signal input secret; 
    signal input path; <span class="hljs-comment">// Indicate whether the data represents the left leaf or the right leaf.</span>
    signal input mainProof[levels];  <span class="hljs-comment">// Construct the data required for deposit root.</span>
    signal input subsetProof[levels]; <span class="hljs-comment">// Construct the data required for allow root.</span>

    <span class="hljs-comment">// Calculate the nullifier and commitment.</span>
    component hasher <span class="hljs-operator">=</span> CommitmentNullifierHasher();
    hasher.secret <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> secret;
    hasher.path <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> path;
    hasher.assetMetadata <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> assetMetadata;
    nullifier <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> hasher.nullifier;

    <span class="hljs-comment">// expectedValue: keccak256("allowed") % p</span>
    component doubleTree <span class="hljs-operator">=</span> DoubleMerkleProof(levels, expectedValue);
    doubleTree.leaf <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> hasher.commitment;
  
    <span class="hljs-comment">// Convert the path to bits to specify whether it is the left leaf or the right leaf. </span>
    <span class="hljs-comment">// It can be observed that the deposit tree and allow tree share the same path.</span>
    doubleTree.path <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> path; 
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> levels; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        doubleTree.mainProof[i] <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> mainProof[i];
        doubleTree.subsetProof[i] <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> subsetProof[i];
    }
    root <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> doubleTree.root; <span class="hljs-comment">// Verify the deposit root.</span>
    subsetRoot <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> doubleTree.subsetRoot; <span class="hljs-comment">// Verify the allow root.</span>

    signal withdrawMetadataSquare;
    withdrawMetadataSquare <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> withdrawMetadata <span class="hljs-operator">*</span> withdrawMetadata;
}
</code></pre><hr><h2 id="h-tldr" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">TL;DR</h2><ul><li><p>&quot;Proof-of-Innocence&quot; is a concept that uses another proof to demonstrate that a withdrawal comes from a deposit listed on the allowed list. It can be constructed from both the perspective of the allowed list and the denied list.</p></li><li><p>Privacy-Pools builds upon TornadoCash and adds the concept of &quot;Proof-of-Innocence.&quot; The receipt in Privacy-Pools represents an additional meaning: proving that the withdrawn funds come from a deposit listed on the allowed list.</p></li><li><p>The Allow Merkle Tree exists to provide proof that the withdrawn funds come from the allowed list. The leaf positions in the Allow Merkle Tree correspond to the deposit leaf positions in the Deposit Merkle Tree. The Allow Merkle Tree leaf data can be &quot;allowed&quot; or &quot;blocked.&quot;</p></li><li><p>In addition to providing the necessary data to construct the Deposit Merkle Root, the withdrawer also needs to provide the Allow Merkle Root and the data required to construct it as proof that the withdrawn funds come from the allowed list.</p></li><li><p>Since the Allow Merkle Root is provided by the withdrawer, it is possible for malicious actors to forge a fake Allow Merkle Root to withdraw illicit funds. However, the presence of a forged Allow Merkle Root would still raise suspicions on the blockchain and enable tracking of the flow of illicit funds.</p></li></ul><p>Developer <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/ameensol">ameen.eth</a> combines the concept of <strong>&quot;Proof-of-Innocence&quot;</strong> with TornadoCash, offering another perspective that <strong>&quot;privacy is not equal to criminality.&quot;</strong> The author finds it interesting to use another Zero-Knowledge Proof (ZKP) to prove an additional fact, akin to the addition of ZKPs. This approach is simpler and more efficient compared to constructing a larger and more complex ZKP. Regarding the choice of the Allow Merkle Tree, it seems that in the future, it may be constructed by a more impartial entity, which would provide greater credibility to others.</p><p>Lastly, the author expresses gratitude to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/@chihchengliang">Chih-Cheng Liang</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://artistic709.medium.com/?source=user_profile-------------------------------------">Ping Chen</a> for reviewing the article and providing valuable insights.</p><hr><h2 id="h-reference" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reference</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/taipei-ethereum-meetup/tornado-cash-%E5%AF%A6%E4%BE%8B%E8%A7%A3%E6%9E%90-eb84db35de04">Tornado Cash 實例解析</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/taipei-ethereum-meetup/zkp-%E8%88%87%E6%99%BA%E8%83%BD%E5%90%88%E7%B4%84%E7%9A%84%E9%96%8B%E7%99%BC%E5%85%A5%E9%96%80-6b2e7ece0714">ZKP 與智能合約的開發入門</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.notion.so/How-to-explain-Tornado-Cash-work-to-your-friends-2c5cb1a5d36c48b3a2109174e34abc26">[ZKP 讀書會] Tornado Cash</a></p></li><li><br></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/tornadocash/tornado-core">https://github.com/tornadocash/tornado-core</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mp.weixin.qq.com/s?__biz=Mzg5MzY2MDEyNw==&amp;mid=2247485672&amp;idx=1&amp;sn=3b7b5c9288dfb4e07d0a86bd0d1d1b4a&amp;chksm=c02a3658f75dbf4e2c5c6f65d632f7244309e463e3cd490af223191d93ca15eeb95110240eb6&amp;token=1045405335&amp;lang=zh_CN#rd">深入理解TornadoCash技术细节</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.privacypools.com/">https://www.privacypools.com/</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ameensol/privacy-pools">https://github.com/ameensol/privacy-pools</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/hhh69251498/status/1633027375398273024">0xhhh Thread 介紹新功能和其原理</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/ameensol/status/1632089405220442116?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1632089405220442116%7Ctwgr%5E4f842e9f290599e85bddc870e01930e488f3af59%7Ctwcon%5Es1_&amp;ref_url=https%3A%2F%2Fthreadreaderapp.com%2Fthread%2F1633027375398273024.html">demo video</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/clip/Ugkx7LeQPvONM0OFOfAUazyjf0JSj_9y7Tqw?embeds_euri=https%3A%2F%2Ftwitter.com%2F&amp;feature=emb_logo">Vitalik’s 講如何改進 Tornado v2</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/ameensol/status/1632089405220442116?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1632089405220442116%7Ctwgr%5E4f842e9f290599e85bddc870e01930e488f3af59%7Ctwcon%5Es1_&amp;ref_url=https%3A%2F%2Fthreadreaderapp.com%2Fthread%2F1633027375398273024.html">privacy pools v0 tweet</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/@chainway_xyz/introducing-proof-of-innocence-built-on-tornado-cash-7336d185cda6">Introducing Proof of Innocence built on Tornado Cash</a></p></li></ul>]]></content:encoded>
            <author>albertlin@newsletter.paragraph.com (Albert.Lin)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/877a76ae99f22d954fe6d70df3e86f2455e28f0add33a28e0f09982e46621091.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Breaking Down TornadoCash: A Beginner’s Guide to Explaining its Functionality to Friends]]></title>
            <link>https://paragraph.com/@albertlin/breaking-down-tornadocash-a-beginner-s-guide-to-explaining-its-functionality-to-friends</link>
            <guid>llqtEYMoEFfZYtjDYkZP</guid>
            <pubDate>Fri, 19 May 2023 12:44:41 GMT</pubDate>
            <description><![CDATA[TornadoCash is a well-known project for anonymous transactions. When a hacking incident occurs, hackers often transfer their funds to TornadoCash to ensure that their money can be moved without being traced. However, in recent years, there has been an increase in the frequency and scale of hacking attacks, which has caught the attention of regulatory authorities in the United States. As a result, a series of regulatory measures have been implemented, such as website shutdowns, arrests of deve...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/46b47e1e3184fe18c8a68407fd9e118e2871dce9967f3f579c201f9f57ea824f.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><p>TornadoCash is a well-known project for anonymous transactions. When a hacking incident occurs, hackers often transfer their funds to TornadoCash to ensure that their money can be moved without being traced. However, in recent years, there has been an increase in the frequency and scale of hacking attacks, which has caught the attention of regulatory authorities in the United States. As a result, a series of regulatory measures have been implemented, such as website shutdowns, arrests of developers, and removal of the source code from GitHub.</p><p>You may be curious about how TornadoCash achieves anonymity. Recently, I conducted some research to understand its operational mechanism. Here, I will do my best to explain in a straightforward manner how TornadoCash maintains anonymity and why they need to do so. I hope this knowledge can help you better understand TornadoCash.</p><hr><h2 id="h-what-does-tornadocash-aim-to-solve" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What does TornadoCash aim to solve?</h2><p>In each transaction, there are typically two parties involved: a <strong>sender</strong> and a <strong>receiver</strong>. TornadoCash aims to conceal the identity of the sender in order to prevent third parties from identifying who initiated the transaction through transaction records. This way, even if the transaction is recorded on the blockchain, the identity of the sender cannot be traced.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7a745fd7b70d32275a6e2b448cbbe6f6d0d6019f048074b1b71e3b789ad9416c.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><h2 id="h-how-does-tornadocash-accomplish-this" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How does TornadoCash accomplish this?</h2><p>TornadoCash utilizes a straightforward and widely adopted method called a <strong>mixer</strong> to achieve anonymous transactions. A mixer is a tool that combines funds from different senders to achieve the goal of anonymity.</p><p>Simply put, a mixer is like a box where senders deposit money, and recipients withdraw money from the box. Because the money is mixed together, it is impossible to determine which specific sender&apos;s money the recipient receives. This method makes the transactions anonymous and prevents the identification of senders from transaction records.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/23f67570b9babbf6ad8fe904fc08d4f50e3f383ab248084f09d417e34e53110c.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><h3 id="h-only-the-person-with-the-receipt-can-claim-the-money" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Only the person with the receipt can claim the money</h3><p>The mixer model needs to address an important question: Who can claim the money? Without a proper mechanism to verify the identity of the recipient, anyone could claim the funds from the box, leading to security risks.</p><p>To solve this problem, TornadoCash employs a straightforward solution, which is the use of <strong>receipts</strong>. A receipt is a document that proves the identity of the recipient, similar to a luggage claim ticket given to hotel guests. Unlike the luggage storage model in real life, TornadoCash&apos;s receipts are not generated by a smart contract but provided by the user upon depositing funds. This is because the logic of the smart contract is stored on the blockchain, visible to anyone. Malicious individuals could replicate the same receipt and attempt to claim funds that do not belong to them.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a17e90a5e8f6d11cb654cfc5191b67b361b4058648d0014504d3a7384f82ee69.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><p>The receipt in TornadoCash is provided by the user, and it is generated by hashing the <code>secret</code> and <code>nullifier</code> together. The <code>secret</code> and <code>nullifier</code>are both chosen and generated by the user. In the TornadoCash Smart Contract, this receipt is referred to as a <code>commitment</code>. However, for the purpose of understanding, we will continue using the term &quot;receipt&quot; here. The <code>nullifier</code> has a special purpose, which we will explain later.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a7a307595d160a385331a02c7d3a210e9d983c2216fb97382101dafead43caed.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><h3 id="h-the-purpose-of-the-receipt" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The purpose of the receipt</h3><p>Implementing the function of receipts on the blockchain is more challenging than imagined. On the blockchain, all information is public, including the receipts generated by Alice. If Bob uses Alice&apos;s receipt to claim funds from the smart contract, it becomes easy for others to know that Bob received the funds originally deposited by Alice, thus failing to achieve the goal of hiding the sender&apos;s identity.</p><p>Therefore, the existing receipt mechanism needs improvement. Before discussing the improvements, let&apos;s first understand the purposes of receipts in the entire system. In fact, receipts serve two main purposes:</p><ol><li><p>Proof of previous sender&apos;s deposit: The receipt serves as proof that the sender has deposited funds previously into the system. It demonstrates that the sender has the authority to claim the funds at a later stage.</p></li><li><p>Ensuring each recipient can claim funds only once: The receipt ensures that each recipient can claim the funds only once, preventing double spending or multiple claims.</p></li></ol><p>To illustrate this using a real-life example of luggage storage, when you have a receipt, it means you have previously deposited your luggage, and you can only retrieve the luggage once. In other words, if we can find a new way to prove that someone has deposited funds into the system and ensure that each recipient can claim the funds only once throughout the system, we can achieve the same effect as receipts.</p><h2 id="h-lets-review-what-we-want-to-prove" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Let&apos;s review what we want to prove.</h2><p>Before explaining how TornadoCash achieves these two objectives, let&apos;s review why we need to do so. If Bob directly uses Alice&apos;s receipt to withdraw money, others can learn from the blockchain that the transaction was initiated by Alice, thus failing to achieve the goal of hiding the sender.</p><p>Therefore, we need to design a method that allows Bob to withdraw money without directly using Alice&apos;s receipt. Instead, he can use some form of proof to claim the funds. This ensures that someone has deposited funds before the withdrawal, and Bob&apos;s proof can only be used once to receive the money. Furthermore, this proof should not reveal any information related to Alice&apos;s receipt. In this way, even if Bob withdraws the money, we cannot determine who sent it to him, thus achieving the goal of hiding the sender. This is the design concept behind TornadoCash.</p><hr><h2 id="h-prove-that-the-sender-has-deposited-funds-before" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prove that the sender has deposited funds before</h2><p>TornadoCash employs two techniques:</p><ol><li><p>Merkle Tree</p></li><li><p>Zero-Knowledge Proof</p></li></ol><h3 id="h-using-merkle-tree-to-record-deposits" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Using Merkle Tree to Record Deposits</h3><p>When users want to deposit money into the Smart Contract, they need to provide a receipt, which is the hash value of the <code>(secret, nullifier)</code> pair mentioned earlier. This receipt, known as a <code>commitment</code>, is considered as a leaf in the Merkle Tree by the TornadoCash Smart Contract and added to the Merkle Tree (as shown in the diagram below, assuming the <code>commitment</code> is placed at leaf node <code>r6</code> in the Merkle Tree).</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/dec2bdd927c67d9e78573ecfdecf8a3dc029e974f3d2f529c1c16bc4002f7058.webp" 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><p>The Merkle Tree is a data structure that allows for the calculation of <code>parent hash</code> values by hashing the leaf data at the bottom layer pairwise. In the provided diagram, for example, the hash value of <code>r5</code> and <code>r6</code> is computed to obtain the <code>parent hash</code> value at the next layer, denoted as <code>H(r5, r6)</code>. This process continues as the parent hash values are hashed pairwise to generate the hash value at the next higher layer. For instance, <code>H(r5, r6)</code> and <code>H(r7, r8)</code> are hashed to obtain <code>H(H(r5, r6)</code>, <code>H(r7, r8))</code>. This process repeats until the Merkle Tree&apos;s root is obtained, which is referred to as the <code>Merkle Root</code>.</p><p>The Merkle Tree data structure possesses a unique characteristic wherein to prove that a particular data is one of the leaves in the Merkle Tree, it is sufficient to provide the series of parent hash values traversed from the leaf to the root. In the given diagram, for instance, to prove that <code>r6</code> is one of the leaves in this Merkle Tree, it is only necessary to provide the data <code>r5</code>, <code>r6</code>, <code>H(r7, r8)</code>, and <code>H(H(r1, r2), H(r3, r4))</code>. These pieces of data are the intermediate parent hash values that establish the inclusion proof for the leaf data in the Merkle Tree.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c9432295402f655006f4cccd0ce8e1eafdbe0af305bc982076aa7e5f24b0b45b.webp" 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><p>Steps:</p><ol><li><p>Calculate the hash value of <code>r5</code> and <code>r6</code> to obtain <code>H(r5, r6)</code>.</p></li><li><p>Calculate the hash value of <code>H(r5, r6)</code> and <code>H(r7, r8)</code> to obtain <code>H(H(r5, r6)</code>, <code>H(r7, r8))</code>.</p></li><li><p>Calculate the hash value of <code>H(H(r1, r2), H(r3, r4))</code> and <code>H(H(r5, r6), H(r7, r8))</code> to obtain the Root.</p></li><li><p>If the calculated Root is the same as the Merkle Root, it proves that <code>r6</code> is one of the leaves in the Merkle Tree.</p></li></ol><h3 id="h-using-zero-knowledge-proofs-zkps-to-hide-deposit-source" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Using Zero-Knowledge Proofs (ZKPs) to Hide Deposit Source</h3><p>The basic concept of Zero-Knowledge Proofs (ZKPs) is to provide a method where users can prove knowledge of certain information without revealing the actual content of that information. For example (the definition may not be precise, but the idea is the same), if I claim to possess the private key for a specific address, I can prove this by sending any amount of cryptocurrency to another address without actually disclosing the private key itself.</p><p>In TornadoCash, ZKP technology is utilized to hide the deposit source. Suppose we want to prove that we made a deposit on TornadoCash (assuming it is at position <code>r6</code>). We can provide the following proof:</p><ul><li><p><strong>Knowledge</strong>: r6, r5, H(r7, r8), and H(H(r1, r2), H(r3, r4))</p></li><li><p><strong>Proof</strong>: Generate a proof using ZKP technology.</p></li><li><p><strong>Verify</strong>: In the verification contract of TornadoCash, calculate the Root based on the Proof data and check if it matches the Merkle Root on the blockchain. If they match, it indicates that r6 is one of the leaves in the TornadoCash Merkle Tree, implying that someone previously deposited funds in TornadoCash.</p></li></ul><p>Using ZKP technology, we can prove that we possess certain specific knowledge without revealing the actual information. In this example, we have demonstrated knowledge of <code>r6</code>, <code>r5</code>, <code>H(r7, r8)</code>, and <code>H(H(r1, r2), H(r3, r4))</code> without disclosing the actual content of these pieces of information. Through such proof, we can hide the source of the deposit, thereby achieving anonymous transactions.</p><h2 id="h-ensuring-each-recipient-can-only-withdraw-funds-once" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Ensuring each recipient can only withdraw funds once</h2><p>To prevent the scenario where assets are withdrawn multiple times or more within a single transaction, TornadoCash introduces the concept of a <code>nullifier</code>. This is a common attack method that could potentially cause losses to TornadoCash. To mitigate this risk, TornadoCash incorporates the use of nullifiers.</p><p>During the deposit process, the <code>nullifier</code> is generated by hashing the <code>secret</code> along with other parameters to create a <code>commitment</code>. When a user wants to make a withdrawal, they must provide the hash value of the <code>nullifier</code> (<code>hash(nullifier)</code>). If this value already exists in the TornadoCash contract, it indicates that the funds associated with this deposit have already been withdrawn, and the withdrawal attempt will fail. However, if the <code>hash(nullifier)</code> has not been recorded, the withdrawal will be considered valid. TornadoCash then records the <code>hash(nullifier)</code> in the contract to ensure that the same deposit cannot be withdrawn again.</p><p>In summary, TornadoCash uses <code>nullifiers</code> to prevent double withdrawal attacks, ensuring the security and reliability of transactions. This mechanism safeguards against the unauthorized duplication of withdrawals and enhances the overall integrity of the system.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/bed413c66609a5ecaf0bac254e376ac16d35aa417fa5a3c78b49b6f46875de70.webp" 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><hr><h2 id="h-tornadocash-withdraw-proof" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">TornadoCash Withdraw Proof</h2><p>Let&apos;s summarize the components included in the final TornadoCash proof:</p><ol><li><p><strong>Nullifier</strong>: The <code>nullifier</code> used during deposit generation to prevent double withdrawal.</p></li><li><p><strong>Secret</strong>: The <code>secret</code> used during deposit generation.</p></li><li><p><strong>Path Elements</strong>: The hash values traversed from the deposit <code>commitment</code> leaf to the Merkle Tree root.</p></li><li><p><strong>Path Indices</strong>: Indicates whether each element in the <code>pathElements</code> is a left or right child.</p></li></ol><p>These components will be used to construct a Zero-Knowledge Proof (ZKP) that allows Bob to prove his entitlement to claim the deposit without revealing his <code>nullifier</code> and <code>secret</code>. When verifying this ZKP, the following public data is required:</p><ol><li><p><strong>Root</strong>: The Merkle Tree root of TornadoCash.</p></li><li><p><strong>NullifierHash</strong>: The hash value of Bob&apos;s <code>nullifier</code>, used to check for the risk of duplicate withdrawals.</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/99499d80cea8b679f778fe7e0689cdc52792d551fbe3a3bb671ca32c88426ed1.webp" 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><p>Based on the provided example, when TornadoCash receives the <code>proof</code> and <code>nullifierHash</code> (assuming Alice&apos;s deposit is at position <code>r6</code>), the following actions are executed:</p><ol><li><p>Using the <code>secret</code> and <code>nullifier</code> from the proof, calculate <code>r6</code>.</p></li><li><p>Calculate the root using <code>r6</code>, <code>r5</code>, <code>H(r7, r8)</code>, and <code>H(H(r1, r2), H(r3, r4))</code>.</p></li><li><p>Compare the calculated <code>root</code> with the <code>Merkle Root</code> stored in TornadoCash. If they match, it indicates a previous deposit. TornadoCash keeps a record of the latest <code>30 Merkle Roots</code> to reduce the chance of transaction failures since the Merkle Root changes with each deposit. The comparison is made against the 30 most recent Merkle Roots.</p></li><li><p>Set the map entry with the key as <code>hash(nullifier)</code> to true to prevent double withdrawal.</p></li><li><p>Transfer 1 ether to Bob.</p></li></ol><p>Steps 1 to 3, which involve validating the proof, are executed in the Zero-Knowledge (ZK) verification process, ensuring that other parties cannot obtain the <code>secret</code>, <code>nullifier</code>, or related information.</p><p>By following these steps, TornadoCash ensures the security and integrity of the withdrawal process, preventing unauthorized withdrawals and maintaining privacy for participants.</p><hr><h2 id="h-tldr" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">TL;DR</h2><ul><li><p>TornadoCash is an anonymous transfer project that uses a mixer to hide the identity of transaction senders.</p></li><li><p>TornadoCash utilizes receipts (<code>commitments</code>) to control access rights. Receipts are generated from the <code>secret</code> and <code>nullifierHash</code>, and each receipt can only be used once.</p></li><li><p>It uses a Merkle Tree to record deposit information, with receipts serving as leaf nodes. The Merkle Root is calculated, and users only need to provide the intermediate data from the leaf to the root to prove that their data is one of the leaves in the Merkle Tree.</p></li><li><p>Zero-Knowledge Proofs are employed to hide the deposit source. <code>Nullifiers</code> are used to prevent Double Withdrawal attacks. TornadoCash proofs require verification by comparing them with public data, such as the <code>Merkle Root</code> and <code>hash(nullifier)</code>.</p></li></ul><p>Lastly, I would like to express my gratitude to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/@martinetlee">Martine</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://antonassocareer.medium.com/">Anton</a> for reviewing the article and providing valuable feedback! Thank you very much for their assistance and contributions!</p><hr><h2 id="h-reference" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reference:</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/taipei-ethereum-meetup/tornado-cash-%E5%AF%A6%E4%BE%8B%E8%A7%A3%E6%9E%90-eb84db35de04">Tornado Cash 實例解析</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/taipei-ethereum-meetup/zkp-%E8%88%87%E6%99%BA%E8%83%BD%E5%90%88%E7%B4%84%E7%9A%84%E9%96%8B%E7%99%BC%E5%85%A5%E9%96%80-6b2e7ece0714">ZKP 與智能合約的開發入門</a></p></li><li><br></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/tornadocash/tornado-core">https://github.com/tornadocash/tornado-core</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mp.weixin.qq.com/s?__biz=Mzg5MzY2MDEyNw==&amp;mid=2247485672&amp;idx=1&amp;sn=3b7b5c9288dfb4e07d0a86bd0d1d1b4a&amp;chksm=c02a3658f75dbf4e2c5c6f65d632f7244309e463e3cd490af223191d93ca15eeb95110240eb6&amp;token=1045405335&amp;lang=zh_CN#rd">深入理解TornadoCash技术细节</a></p></li></ul>]]></content:encoded>
            <author>albertlin@newsletter.paragraph.com (Albert.Lin)</author>
        </item>
        <item>
            <title><![CDATA[EigenLayer Restaking Explainer]]></title>
            <link>https://paragraph.com/@albertlin/eigenlayer-restaking-explainer</link>
            <guid>aLG45yIGoklJk0FjsNQa</guid>
            <pubDate>Wed, 12 Apr 2023 14:58:58 GMT</pubDate>
            <description><![CDATA[As the Ethereum Shanghai upgrade draws near, there&apos;s been a growing interest in the LSD (Liquid Staking Derivatives) sector. EigenLayer, which recently released a white paper, has been the center of attention. 『 Restaking 』 is the core concept of EigenLayer. When I first learned about EigenLayer, Restaking was the biggest issue I had trouble understanding. However, after conducting research, I&apos;ve gained some preliminary understanding. In this article, I hope to help readers who shar...]]></description>
            <content:encoded><![CDATA[<p>As the Ethereum Shanghai upgrade draws near, there&apos;s been a growing interest in the LSD (Liquid Staking Derivatives) sector. <strong>EigenLayer</strong>, which recently released a white paper, has been the center of attention. 『 <strong>Restaking 』</strong> is the core concept of EigenLayer. When I first learned about EigenLayer, <strong>Restaking</strong> was the biggest issue I had trouble understanding. However, after conducting research, I&apos;ve gained some preliminary understanding. In this article, I hope to help readers who share the same doubts as me to understand better the origin of Restaking and the problems it aims to solve.</p><h2 id="h-evolution-of-trust-in-blockchain" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Evolution of Trust in Blockchain</h2><p>Before we dive into what Restaking is, let&apos;s first review the practical meaning and evolution of trust in Blockchain.</p><h3 id="h-bitcoin-and-decentralized-trust" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Bitcoin and Decentralized Trust</h3><p>Bitcoin, proposed by Satoshi Nakamoto, is a decentralized trust structure. This trust comes from the fact that each miner has their own ledger. Even if a malicious miner tries to modify their ledger, it will still be rejected by other honest miners.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5e170130721438b7e74a4ebeb1b39c4753c446e8f3f027216bdd256ec82efe5e.png" alt="The top-level concept of Limited Programmability refers to Bitcoin&apos;s ability to provide a scripting language for simple logic, but it cannot support complex programs. (https://www.youtube.com/watch?v=-V-fG4J1N_MM)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The top-level concept of Limited Programmability refers to Bitcoin&apos;s ability to provide a scripting language for simple logic, but it cannot support complex programs. (https://www.youtube.com/watch?v=-V-fG4J1N_MM)</figcaption></figure><p>The above diagram simplifies the Bitcoin architecture into three layers. The bottom layer represents miners who operate the entire Bitcoin network. The middle layer represents miners who follow a set of PoW (Proof of Work) rules to generate the next block. The top layer (Limited Programmability) refers to the application services that run on this network, mainly for <strong>peer-to-peer transfers</strong>. We trust that the <strong>peer-to-peer transfer</strong> service at the top layer is correct mainly because we trust that the miners at the bottom all follow the same rules (PoW) to generate blocks (modify ledgers), and there are no malicious modifications in the middle layer (if there are, they will not be recognized by other miners). This is a bottom-up trust process.</p><p>In the early days, if one wanted to establish another decentralized application service, they would have to replicate the same architecture. In order to incentivize miners to operate the decentralized network in a specific way, miners would follow certain rules together, ultimately allowing decentralized application services to run on this decentralized network. As shown in the diagram below, this is why early new decentralized services corresponded to a new chain.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/87a90aed7e3e94dced42ad60754cdf9195147474104c6bc2f5a06c6b04786863.png" alt="In the early days, if you wanted to create different decentralized networks, you needed to establish your own blockchain network. (https://www.youtube.com/watch?v=-V-fG4J1N_MM)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">In the early days, if you wanted to create different decentralized networks, you needed to establish your own blockchain network. (https://www.youtube.com/watch?v=-V-fG4J1N_MM)</figcaption></figure><h3 id="h-modular-trust-in-ethereum" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Modular Trust in Ethereum</h3><p>Until the emergence of Ethereum, there were few innovations in the trust structure. The biggest difference between Ethereum and Bitcoin&apos;s trust structure is that the top layer is no longer limited to a script language. Instead, it is a Turing-complete program executor: <strong>EVM (Ethereum Virtual Machine)</strong>. With EVM, anyone can create their own decentralized services (DApps) on the Ethereum network, such as <code>AAVE</code>, <code>ENS</code>, or <code>Uniswap</code>. These decentralized services are executed through EVM, and the correctness of EVM execution results is ensured by the nodes in the lower layer (following the PoS consensus rules). Through this mechanism, people who want to build different DApps no longer need to establish another decentralized network to gain trust, but can directly use Ethereum&apos;s trust network, which means that trust is modularized. This modularized trust structure significantly reduces the threshold for DApp innovation because there is no need to build another decentralized network for a specific DApp.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1c594e1b3fb0bfb851a3cd7a2acef9df0d4e0972d79d27e898f7525933316b71.png" alt="Ethereum introduced the EVM architecture, which allows for the operation of various DApps on top of it, significantly enhancing innovation in decentralized networks. (https://www.youtube.com/watch?v=-V-fG4J1N_MM)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Ethereum introduced the EVM architecture, which allows for the operation of various DApps on top of it, significantly enhancing innovation in decentralized networks. (https://www.youtube.com/watch?v=-V-fG4J1N_MM)</figcaption></figure><hr><h2 id="h-barries-to-open-innovation-middlewares" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Barries to open innovation: Middlewares</h2><p>However, there are still some issues with this modularized trust. The main reason is that Ethereum can only provide <strong>block-making-level</strong> trust. Block-making trust mainly refers to two things:</p><ul><li><p><strong>Transaction ordering</strong></p></li><li><p><strong>Transaction execution</strong></p></li></ul><p>If services outside of on-chain transactions are required, trust cannot be obtained from the Ethereum network (because Ethereum can only guarantee that transactions will be executed correctly according to the contract code). Usually, such services still need to establish another (decentralized) network of trust. Most of these types of services are also part of a DApp system, which are collectively referred to as <strong>Middlewares</strong>.</p><p>To ensure the security of Middlewares, a mechanism needs to be designed to ensure the correctness of data (to ensure that data is not maliciously tampered with). The most common situation is to design a high-yield economic model that encourages others to pledge to ensure the security of the service (e.g., <code>Chainlink</code> Staking). This makes the main cost of Middlewares to maintain a high APR an economical cost rather than an operational cost. Any service that has its own distributed verification mechanism for verification is called <strong>Actively Validated Services (AVS)</strong>. The above use of token economic mechanisms to ensure security is just one method. Other methods, such as optimistic verification used by <code>Nomad</code> cross-chain bridges, and <code>Wormhole/Axie</code> cross-chain bridges that ensure security through trusting a few specific authorized relayers, are all trust issues that EigenLayer hopes to solve through Restaking.</p><p>Usually, a large DApp will not only run its own services but also need to integrate different Middlewares (e.g., Oracle, Bridge, etc.). In the case where each Middlewares has its own AVS to ensure the credibility of the data, the situation is as shown in the following diagram.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ae298bab73f00d5b39f41a920c0b7031e420031b92c313df486ba799e1c948a5.png" alt="Due to the composability of DApps, a decentralized service typically includes various middleware. (https://docs.eigenlayer.xyz/whitepaper.pdf)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Due to the composability of DApps, a decentralized service typically includes various middleware. (https://docs.eigenlayer.xyz/whitepaper.pdf)</figcaption></figure><h2 id="h-the-problems-for-current-daapp" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Problems for Current DAapp</h2><p>Currently, there are some issues with the DApp architecture:</p><ul><li><p>AVS cannot inherit trust from existing ETH. Ethereum itself already has a trust mechanism in place, and Middlewares cannot obtain trust (security) from this mechanism and need to create another trust network (AVS).</p></li><li><p>The threshold for building a new full-trust network (AVS) from scratch is very high.</p></li><li><p>Each AVS has its own pool, and Stakers need to pay additional fees to these pools besides paying Ethereum transaction fees. Using EigenLayer, there is no need to pay additional fees to these pools, or in other words, transaction fees are transferred to Ethereum validators. Under the original architecture, Ethereum validators cannot obtain these fees. This problem is referred to as Ethereum&apos;s Value Leakage.</p></li><li><p>AVS will become the weakest link in the security of the DApp system. Because compared to disrupting the Ethereum network, the cost of disrupting AVS will be lower (using the example in the above diagram: Ethereum network: 10B, other AVS: 1B).</p></li></ul><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/89214033b4b8bca046361b29aeb6cb5daf505313edcb1055810b37de8bc3168f.png" alt="he bottleneck of system security often exists in the most vulnerable parts, and as a result, AVS is more likely to become the target of attacks. (https://docs.eigenlayer.xyz/whitepaper.pdf)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">he bottleneck of system security often exists in the most vulnerable parts, and as a result, AVS is more likely to become the target of attacks. (https://docs.eigenlayer.xyz/whitepaper.pdf)</figcaption></figure><hr><h2 id="h-eigenlayer-solution" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">EigenLayer Solution</h2><p>EigenLayer proposes two solutions:</p><ul><li><p>Pooled Security via Restaking</p></li><li><p>Free-market governance</p></li></ul><h3 id="h-pooled-security-via-restaking" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Pooled Security via Restaking</h3><p>Allow the same <strong>funds</strong> to be staked in <strong>different trust (decentralized) networks</strong> to secure the data accuracy of different trust networks.</p><p>In practice, the staked funds are those of Ethereum network validators, which are also used by EigenLayer to stake in other Middlewares&apos; trust networks.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/51a7ce6a812ce8b5c9cdbffce677e3a061f21ef9678133edc97ea974e7eceb38.png" alt="Stakers can first stake Ether to EigenLayer and decide which AVS to stake." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Stakers can first stake Ether to EigenLayer and decide which AVS to stake.</figcaption></figure><p><strong>For Stakers</strong></p><ul><li><p>By staking the same funds in different trust networks and serving as validators for different networks, validators can earn additional validation rewards. However, if a validator engages in malicious behavior that harms the AVS, the staked capital will be punished and reduced (<strong>Shashing</strong>), which is the risk that validators must bear to earn additional rewards.</p></li><li><p>The cost of maintaining the security of DApps is lower because there is no need to prepare two sets of funds to ensure the security of both Ethereum and Middlewares.</p></li></ul><p><strong>For Middlewares</strong></p><ul><li><p>The threshold for establishing a trust network is significantly lowered. It is possible to inherit part or all of the trust (funds) from the Ethereum network, without the need to create a new trust network from scratch.</p></li><li><p>Enhancing the security of AVS. Because everyone can share the trust brought by the same funds. In the original structure shown in the diagram, each AVS can only provide 1B worth of trust, but by switching to the EigenLayer Restaking architecture, each AVS inherits the overall 13B worth of trust, greatly enhancing security.</p></li></ul><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7e87c5448aa430d0f7cfc0d1790bb1f46782e52e8bdb11991db7f575dff75b5c.png" alt="Under the Restaking framework, the funds that were originally scattered across different AVS are now consolidated to collectively provide security and trust for these AVS and Ethereum. (https://docs.eigenlayer.xyz/whitepaper.pdf)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Under the Restaking framework, the funds that were originally scattered across different AVS are now consolidated to collectively provide security and trust for these AVS and Ethereum. (https://docs.eigenlayer.xyz/whitepaper.pdf)</figcaption></figure><h3 id="h-free-market-governance" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Free-market governance</h3><p>Basically, it can be divided into two blocks: <strong>Staker</strong> (left) and <strong>AVS</strong> (right). Stakers can freely choose which AVS they want to participate in staking and receive rewards from. Each AVS can decide which rewards to offer to the Stakers who participate in staking, according to their own needs. This creates a free market for trading between Stakers and AVS.</p><p><strong>For Stakers</strong>, the marginal cost of participating in staking with an additional AVS is very low. They only need to use the same capital to obtain additional rewards, without requiring additional capital. In theory, the more AVS that they participate in staking with, the higher their potential profit. Although participating in staking with other AVS does not require additional costs, they still need to bear corresponding risks. If, due to uncontrollable factors or malicious behavior harmful to AVS during the staking process, the staked funds will be reduced due to punishment (Slashing). For Stakers, as long as the total reward is greater than the total risk, they should stake more AVS as much as possible.</p><p><strong>For AVS</strong> (in this case, Ethereum PoS is regarded as a type of AVS), in addition to being able to decide on the reward method themselves, they can also set conditions and restrictions for Stakers. Only Stakers who meet certain qualifications can stake and receive rewards on that AVS. For example, a certain AVS may require that the number of AVS staked by the Staker be less than a specific amount before they can stake on that AVS (in order to reduce the risk of operator collusion caused by staking too many AVS, which will be explained further in the article).</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b998ec50fe553a9b976422c1f38d8389abeff18069f87bf27fabbde22fb3a35c.png" alt="In a free-market, Stakers have the freedom to choose which AVS to stake on, taking on risk and earning additional rewards." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">In a free-market, Stakers have the freedom to choose which AVS to stake on, taking on risk and earning additional rewards.</figcaption></figure><h2 id="h-restaking-vs-merge-mining" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Restaking vs Merge Mining</h2><p>The concept of Restaking is similar to early Merge Mining. The concept of Merge Mining is that, in the early days of PoW, the same mining machine is used to serve as a miner for different PoW networks, using the same computing power to obtain mining fees from different networks.</p><p><strong>Merge Mining</strong> has a significant drawback in that it cannot effectively punish malicious miners. For example, a miner mines both Bitcoin and AppleCoin (generating blocks) at the same time. The miner intentionally engages in malicious behavior on AppleCoin to try to profit, but is later discovered and not recognized by other miners on the AppleCoin network, resulting in failure. The actual loss to the miner is not significant, as the malicious behavior on AppleCoin does not affect the miner&apos;s earnings on other PoW networks (Bitcoin&apos;s price is not directly affected), and the miner&apos;s mining machine can still continue to mine on other PoW networks to earn profits. The miner only loses the mining fee originally earned on AppleCoin and the electricity cost incurred for mining AppleCoin, without any additional losses.</p><p>When Ethereum transitions from PoW to PoS, if validators engage in wrongdoing, the ETH they staked will be punished and reduced. This is also the case for other AVS, where bad behavior will reduce the staked funds. This effectively reduces malicious behavior by validators. If the rules of other AVS can be written into smart contracts, then <strong>slashing (effective punishment)</strong> can be achieved. This is the main difference between Restaking and Merge Mining, and also the main reason why the Restaking system can operate.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/cd129289c6d25baac27ef252a9ae40c124cb78ff92cb919ada36afdf98f28e1a.png" alt="https://docs.eigenlayer.xyz/whitepaper.pdf" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">https://docs.eigenlayer.xyz/whitepaper.pdf</figcaption></figure><h2 id="h-risk" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Risk</h2><p>The Restaking proposed by EigenLayer does solve the above-mentioned problems, but it also brings some new ones. The EigenLayer whitepaper lists two potential risks.</p><ul><li><p>Operator collusion</p></li><li><p>Unintended slashing</p></li></ul><h3 id="h-operator-collusion" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Operator collusion</h3><p>For a Staker, staking with all AVS is the most profitable strategy, but this may lead to a situation where <code>All AVS TVL &gt; Cryptoeconomic Security</code>.</p><p>Assuming that a Staker stakes a total of 8M, but participates in staking with 5 AVS with a TVL of 2M each. This situation may lead to the Staker collaborating with other Stakers to attack all AVS in order to obtain greater benefits. The concept is similar to gambling in a casino, using the same bet to place bets on multiple different games. Although there is a chance of losing all the bets, there is also a chance of winning a higher prize than the bet. This means that if the attacker succeeds in the attack, they can also obtain funds that are more than the staking cost.</p><blockquote><p><code>All AVS TVL (2M * 5 = 10M) &gt; Cryptoeconomic Security（8Ｍ）</code></p></blockquote><p>EigenLayer&apos;s proposed solution is as follows:</p><ul><li><p>AVS can set their own limiting conditions to reduce the profitability of attacks. The purpose is to reduce the profit that attackers can obtain through attacks. For example, during the Slashing period of Bridge Middleware, an upper limit on the total amount of transmission can be specified.</p></li><li><p>Propose a monitoring system to track the AVS in which the Staker participates, in order to limit the number of AVS that the Staker can participate in.</p></li></ul><h3 id="h-unintended-slashing" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Unintended Slashing</h3><p>This refers to situations where the staked capital is slashed due to factors that are not intentionally malicious, but rather caused by other unpredictable factors (e.g., node misconfiguration). The proposed solution here is that EigenLayer will establish a committee that can vote on whether to cancel the slashing in specific situations. The members of the committee will be individuals with high reputations in the Ethereum and EigenLayer communities. This approach serves as a short-term solution and transitional arrangement until each AVS is integrated into EigenLayer and reaches maturity.</p><hr><h2 id="h-tldr" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">TLDR</h2><ul><li><p>Ethereum has proposed a new modular trust innovation, which allows for the development of DApps without the need to create a new decentralized network. Developers can use the trust provided by the Ethereum network directly.</p></li><li><p>Ethereum only provides trust at the block generation level, and Middlewares cannot use Ethereum&apos;s trust mechanism to build broader decentralized services. Middlewares still need to create their own trust network (AVS), with the main cost being the economic model for maintaining network security.</p></li><li><p>EigenLayer is a platform that aims to explore the Liquid Staking Derivatives sector and provide more secure and reliable decentralized services. Its core concept is Restaking, which allows the same capital to be staked in different trust networks to ensure the security of different networks.</p></li><li><p>Pooled Security via Restaking allows stakers to earn more rewards while reducing the cost of maintaining DApp security. Through Restaking, stakers can stake the same capital in different trust networks and act as validators for different networks to earn rewards. Restaking can also significantly lower the threshold for building trust networks for Middlewares, allowing them to inherit some or all of Ethereum&apos;s trust (capital).</p></li><li><p>Free-market governance establishes an open market mechanism, where stakers can choose who to stake based on returns and risks. Middlewares can restrict stakers based on their needs to ensure the security of their own network.</p></li><li><p>EigenLayer&apos;s innovation also brings additional problems and risks, such as Operator collusion and Unintended Slashing. Different approaches need to be adopted to address these issues based on different use cases.</p></li></ul><p>When we first encounter Restaking, it is easy to think of it as just another service similar to LSD. However, by understanding the evolution from Bitcoin&apos;s early trust architecture to Ethereum&apos;s modular trust and Restaking, we can truly understand the problems that EigenLayer wants to solve. In summary, EigenLayer&apos;s solution can bring higher security and reliability to decentralized services while reducing the cost of maintaining DApp security, benefiting both stakers and Middlewares. I hope this article can help readers with similar questions to understand the meaning behind Restaking more deeply. After understanding its meaning, reading EigenLayer&apos;s whitepaper should make its content clearer.</p><p>I would like to express our special thanks to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/@twedusuck">NIC</a> for reviewing and correcting this article, which has greatly improved its quality.</p><hr><h2 id="h-reference" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reference</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.eigenlayer.xyz/overview/whitepaper">白皮書</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/eigenlayer/status/1627887300448485376?s=20">Eigenlayer 白皮書Tweet</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/100y_eth/status/1628037520113156096?s=20">100y_eth Summary</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://foresightnews.pro/article/detail/22860">EigenLayer：展望以太坊生态的下一个主流叙事</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://foresightnews.pro/article/detail/22729">EigenLayer：2023 以太坊生态新叙事</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/7upDAO/status/1626497846953533441?s=20">https://twitter.com/7upDAO/status/1626497846953533441?s=20</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/OlimpioCrypto/status/1610013658125328384">https://twitter.com/OlimpioCrypto/status/1610013658125328384</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x5Eba828AB4999825D8416D7EAd9563b64FD90276/nRNyWdsSYiv77qiVXBXqG5f8JfyK-3RzRDx9cklPAGU">IOSG Weekly Brief | EigenLayer：将以太坊级别的信任引入中间件 #149</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/shorts/EQbogr_Th3w">Eigenlayer：以太坊又一重磅赛道推荐，二次质押关注起</a></p></li><li><br></li><li><br></li></ul>]]></content:encoded>
            <author>albertlin@newsletter.paragraph.com (Albert.Lin)</author>
        </item>
        <item>
            <title><![CDATA[Introduction to the Principles and Architecture of Curve Stablecoin]]></title>
            <link>https://paragraph.com/@albertlin/introduction-to-the-principles-and-architecture-of-curve-stablecoin</link>
            <guid>NDJ3gNoWo9mASTJRMdOR</guid>
            <pubDate>Sat, 04 Mar 2023 06:19:54 GMT</pubDate>
            <description><![CDATA[Curve Finance is a highly representative innovation in the DeFi field, whether it&apos;s in the AMM algorithm for Stablecoin Swap or the veToken economic model design, which is widely imitated by others. At the end of last year, Curve Finance proposed their stablecoin white paper, which made people curious about what new features they would offer to the DeFi ecosystem this time. During the research process, there were some insights and understanding that I hope can help everyone better unders...]]></description>
            <content:encoded><![CDATA[<p>Curve Finance is a highly representative innovation in the DeFi field, whether it&apos;s in the AMM algorithm for Stablecoin Swap or the veToken economic model design, which is widely imitated by others. At the end of last year, Curve Finance proposed their stablecoin white paper, which made people curious about what new features they would offer to the DeFi ecosystem this time. During the research process, there were some insights and understanding that I hope can help everyone better understand the design of Curve stablecoins.</p><hr><h2 id="h-types-of-curve-stablecoins-and-their-problem-solving-features" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Types of Curve Stablecoins and Their Problem-Solving Features</h2><p>Curve Finance&apos;s stablecoin design belongs to the type of over-collateralized stablecoins. This type of stablecoin is minted (or borrowed) by using other assets such as ETH as collateral, based on a specific interest rate and the value of the collateral. The most famous protocol of this type is MakerDAO&apos;s DAI. ETH is used as collateral and borrowed to obtain MakerDAO&apos;s stablecoin DAI. Here, the collateral is changed to Curve Finance to borrow Curve stablecoins.</p><p>Curve stablecoins are designed to solve the problem of price volatility that exists in many cryptocurrencies. The value of stablecoins is stable and not subject to fluctuations in market prices, making them ideal for use as a medium of exchange or a store of value. In addition, the use of over-collateralization reduces the risk of liquidation and instability caused by fluctuations in the value of the collateral. Overall, Curve stablecoins provide a more secure and stable solution for users in the DeFi ecosystem.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/04949dcad77a2ac62de82ce684274e4319d0ef187a7350c8c7fc3b97a89aec1a.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><h3 id="h-liquidation-issues" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Liquidation Issues</h3><p>Stablecoin architectures of this type always have a liquidation mechanism. Liquidation refers to the situation when the value of the collateral falls to a certain price, causing the value of the collateral to no longer support the stablecoins borrowed. At this time, the collateral (partially or wholly) will be auctioned off at a favorable price to fill the gap in borrowed stablecoins. However, the current liquidation model has several problems:</p><ul><li><p>When a whale&apos;s assets are liquidated, many collaterals will flow into the market, causing market fluctuations.</p></li><li><p>In most cases, liquidators will sell the liquidated collateral to profit, causing the collateral&apos;s price to drop again, resulting in another wave of liquidation.</p></li><li><p>Liquidators usually sell the auctioned collateral on CEX/DEX. If the exchange lacks liquidity, it will cause bad debts for the lending platform.</p></li><li><p>Users who are liquidated will suffer permanent losses. Even if the collateral&apos;s price rebounds, the collateral has been liquidated and cannot be restored.</p></li></ul><p>In view of these issues, Curve hopes to improve the liquidation problem in stablecoin design in the following ways:</p><ul><li><p>Do not rely on external DEXs, as DEX liquidity is uncontrollable.</p></li><li><p>Reduce users&apos; losses as much as possible when liquidation occurs.</p></li><li><p>Gradually liquidate users&apos; collateral and convert it back when the price rebounds.</p></li></ul><hr><h2 id="h-curve-stablecoin-overall-architecture" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Curve Stablecoin Overall Architecture</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9ae53ea8e8688d9784566490035038eded112e13c41d656a3a967d659c046539.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><p>The above diagram is the architecture diagram in the white paper, which can be roughly divided into two blocks.</p><ul><li><p>The Controller and LLAMMA are mainly responsible for collateral liquidation-related work (the blue box in the above figure).</p></li><li><p>Monetary Policy, PegKeeper, and Stable Pool are mainly responsible for anchoring stablecoins to 1 USD-related work (the green box in the above figure).</p></li></ul><p>This article will mainly focus on the introduction of Controller and LLAMMA, but there will also be a brief explanation of the Monetary Policy-related work. So let&apos;s start with today&apos;s highlight, LLAMMA!</p><h3 id="h-llamma-lending-liquidating-amm-algorithm" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">LLAMMA (Lending-Liquidating AMM Algorithm)</h3><p>LLAMMA stands for Lending-Liquidating AMM Algorithm, which, as the name suggests, is an AMM algorithm related to lending and liquidation. Curve mainly uses this algorithm to liquidate collateral.</p><p>AMM represents a Swap Pool based on the LLAMMA algorithm, which we will call the LLAMMA Pool for now. Users can deposit their collateral into the LLAMMA Pool through the Controller and mint stablecoins. The assets in the LLAMMA Pool are collateral and Curve stablecoins. In this article, we will use crvUSD to refer to Curve stablecoins and ETH as collateral for illustration purposes.</p><p>The most significant feature of the LLAMMA Pool is that when the price of collateral (ETH) falls below a specific price, the collateral in the Pool will gradually turn into crvUSD. Conversely, when the price is higher than a specific price, crvUSD will gradually turn into collateral (ETH).</p><p>It sounds quite magical! Before understanding this conversion process, we need to know what happens when users put their collateral in the Pool. The following diagram is an illustration of the interaction between various contracts, giving a clearer concept of the role played by the LLAMMA Pool in the architecture.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2346d293459f2612aaba7259ed079e9cace5b9b50ab7ff14054fb0efc673d57a.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><h2 id="h-dynamic-range-and-oracle-price" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Dynamic Range and Oracle Price</h2><h3 id="h-band" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Band</h3><p>First, LLAMMA divides the collateral price range into different price segments called bands. When users deposit collateral, they need to provide:</p><ul><li><p>The amount of collateral</p></li><li><p>How many bands to store the collateral in</p></li><li><p>How much crvUSD to mint (or borrow)</p></li></ul><p>The collateral will be divided into equal and evenly divided small pieces according to the number of bands to store, and then stored in a continuous price range composed of bands based on the current price and the minted crvUSD. The following figure shows the distribution of storing 10 ETH in 5 bands after calculation, with 5 small pieces of 2 ETH each stored in 5 consecutive bands.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a650a956098f5322634d59d73a950d7cb0ad223039ab66a56693a6093306247d.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><p>In addition, it should be noted that each band has an upper price (<code>P_UP</code>) and lower price (<code>P_DOWN</code>). The upper price of the previous band is also the lower price of the next band. For example, the upper price of <code>band 0</code> in the above figure (3000) is also the lower price of <code>band -1</code> (3000). The upper and lower prices of each band represent the start and end prices of liquidation for that band. When the price reaches the upper price, the collateral stored in that band will begin to be liquidated until the price reaches the lower price, indicating the end of liquidation (meaning that all collateral in that band has been converted to crvUSD).</p><h3 id="h-oracle-price" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Oracle Price</h3><p>In LLAMMA, there is another important role, which is the Oracle Price. The Oracle Price refers to the external collateral price (which can be thought of as the current market price). LLAMMA designs the Oracle Price role to achieve the behavior of ETH &lt;-&gt; crvUSD conversion at a specific price for the collateral. We will explain how this is achieved in detail in the following chapters. Here, we assume that the Oracle Price is <code>P_ORACLE</code>, and the price in the LLAMMA Pool is assumed to be <code>P_AMM</code>.</p><p>Regarding the design of the Oracle, what is certain is that each time an external price is obtained, it will be processed by EMA before being used. This is to prevent users from suffering losses due to drastic fluctuations in external prices while also increasing the difficulty of manipulating the Oracle Price. As the service is not yet online, it is not certain which service will be used as the Oracle source.</p><h2 id="h-how-to-work-in-llamma-pool" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How to Work in LLAMMA Pool</h2><p>The figure below shows the price changes in the band range (using ETH as collateral). When the ETH price is higher than <code>P_UP</code> (yellow range), all assets in the band range will be converted to ETH. When the ETH price is lower than <code>P_DOWN</code> (green range), all assets in the band range will be converted to crvUSD. When the price is between <code>P_UP</code> and <code>P_DOWN</code> (white range), the assets will be in a partially ETH and partially crvUSD state, with the proportion of ETH and crvUSD determined by the price changes.</p><p>The purple line in the middle indicates the price changes of <code>P_ORACLE</code>, and the red line indicates the price changes of <code>P_AMM</code>. <code>P_CD</code> and <code>P_CU</code> can be thought of as two equations created to find the upper and lower prices of this band.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7fde6f70d7af514819dfb12961579cb57153abe9395c5922bcb87c3a64811267.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><p>When <code>P_ORACLE</code> and <code>P_AMM</code> are equal, it will be the blue point in the middle of the diagram, indicating that the ETH price in the Pool is consistent with the external price.</p><h3 id="h-oracle-price-increase" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Oracle Price Increase</h3><p>From the diagram of LLAMMA pool price, it can be observed that when the price starts to rise (the orange line in the diagram), the red line (<code>P_AMM</code>) rises faster than the purple line (<code>P_ORACLE</code>). When <code>P_AMM</code> &gt; <code>P_ORACLE</code>, an arbitrage space is created, motivating external arbitrageurs to deposit ETH into the LLAMMA Pool to exchange for more crvUSD to profit until <code>P_AMM</code> = <code>P_ORACLE</code> is balanced again. During this process, the amount of ETH in the pool gradually increases, while the amount of crvUSD gradually decreases. When it goes higher than <code>P_UP</code>, only ETH will remain in this range. The figure below shows the changes in the AMM price (<code>P_AMM</code>) during the arbitrage process (the X-axis represents the oracle price, and the Y-axis represents the AMM price. To clearly indicate the price position, the orange line of the oracle price is listed on both the X-axis and the Y-axis).</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/615c192f9641cdd447baf4730227ea9cf903d9a54cbae8df4150aa1b21e52a57.gif" alt="When P_AMM &gt; P_ORACLE, arbitrageurs will start depositing ETH and taking out crvUSD, and the P_AMM line (red) will begin to move to the right until P_AMM = P_ORACLE is balanced again." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">When P_AMM &gt; P_ORACLE, arbitrageurs will start depositing ETH and taking out crvUSD, and the P_AMM line (red) will begin to move to the right until P_AMM = P_ORACLE is balanced again.</figcaption></figure><h3 id="h-oracle-price-decrease" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Oracle Price Decrease</h3><p>Conversely, when the price starts to fall (the green line in the LLAMMA pool price diagram), it can be observed that the red line (<code>P_AMM</code>) falls faster than the purple line (<code>P_ORACLE</code>). When <code>P_AMM</code> &lt; <code>P_ORACLE</code>, an arbitrage space is also created, motivating external arbitrageurs to deposit crvUSD to exchange for more ETH until the price is balanced again. During this process, the amount of crvUSD in the pool gradually increases, while the amount of ETH gradually decreases. When it goes lower than <code>P_DOWN</code>, only crvUSD will remain in this range.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2482a85d8c608a36dfee12eb3ab4e025c4209766e153cb29e605b15a704bcf23.gif" alt="When P_AMM &lt; P_ORACLE, arbitrageurs will start depositing crvUSD and taking out ETH, and the P_AMM line (red) will begin to move to the left until P_AMM = P_ORACLE is balanced again. (X-axis: oracle price, Y-axis: AMM price, orange line: oracle price)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">When P_AMM &lt; P_ORACLE, arbitrageurs will start depositing crvUSD and taking out ETH, and the P_AMM line (red) will begin to move to the left until P_AMM = P_ORACLE is balanced again. (X-axis: oracle price, Y-axis: AMM price, orange line: oracle price)</figcaption></figure><h2 id="h-llaama-formulas" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">LLAAMA Formulas</h2><p>Why does<code> P_AMM</code> rise or fall faster than <code>P_ORACLE</code>? To understand this, we need to look at the formula for <code>P_AMM</code>. In the LLAMMA Invariant formula, I, f, and g are all related to <code>P_ORACLE</code>. This means that <code>I</code>, <code>f</code>, and <code>g</code> are not constant, but will change.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b90c40b7fd69d3ee64692393333997b483c582d7dd63790ebbe10234ccd49701.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><p><code>x</code> and <code>y</code> refer to the USD balance and ETH balance of the band range. <code>y_0</code> is the ETH balance when <code>P_AMM = P_ORACLE = P_UP</code>, which can be temporarily viewed as a constant (in reality, it is also a function related to <code>P_ORACLE</code>).</p><p>If we arrange the band range as a sequence, we will find that it is a geometric sequence. <code>A</code> is the number that determines the ratio of this geometric sequence, and it is decided when the contract is deployed, so <code>A</code> is also an invariant.</p><p>It can be seen that <code>P_AMM</code> is only related to the parameters <code>f</code> and <code>g</code>, and <code>f</code> and <code>g</code> are only related to the changes in <code>P_ORACLE</code>. When <code>P_ORACLE</code> rises, <code>f</code> value will rise (square relationship will rise faster) and <code>g</code> value will decrease. The rise of <code>P_AMM</code> will be greater than that of <code>P_ORACLE</code>, resulting in <code>P_AMM &gt; P_ORACLE</code>. When <code>P_ORACLE</code> falls, <code>f</code> value will decrease (square relationship will decrease faster) and <code>g</code> value will increase. The fall of <code>P_AMM</code> will be greater than that of <code>P_ORACLE</code>, resulting in <code>P_AMM &lt; P_ORACLE</code>. Even if there is no swap in the LLAMMA pool, <code>P_AMM</code> will change with <code>P_ORACLE</code>, and the magnitude of <code>P_AMM</code> will be greater than that of <code>P_ORACLE</code>, thus creating an arbitrage opportunity to balance the asset ratio of the pool.</p><h2 id="h-summary-of-llamma" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Summary of LLAMMA</h2><p>LLAMMA intentionally creates an arbitrage opportunity for external traders by utilizing the fact that <code>P_AMM</code> fluctuates more than <code>P_ORACLE</code>. When arbitrageurs attempt to rebalance the asset ratio in the pool, they are essentially performing partial liquidations of the pool&apos;s assets. The advantage of this dynamic and continuous liquidation process is that it avoids a large-scale liquidation that could cause market volatility. As prices recover, the assets will convert back to collateral without causing permanent losses to the collateral.</p><hr><h2 id="h-stabilizer-and-monetary-policy" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Stabilizer and Monetary Policy</h2><p>After discussing how LLAMMA solves the collateral liquidation problem, let&apos;s briefly explain what PegKeeper and Monetary Policy are and how they work. PegKeeper and Monetary Policy are the mechanisms used by Curve Finance to anchor crvUSD to 1 USD.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/448a924b07a339a220fa55868ac99d88459c7ac88d87a35c480cb3a6ffb5210b.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><p><strong>When crvUSD price &gt; 1 USD:</strong> it means there is a shortage of crvUSD in the Curve pool. PegKeeper can mint new crvUSD without collateral and deposit it into the Curve Pool to increase the supply of crvUSD in the market.</p><p><strong>When crvUSD price &lt; 1 USD:</strong> it means there is an excess of crvUSD in the Curve pool. PegKeeper will start withdrawing previously minted crvUSD from the Curve Pool and burning these crvUSD to reduce the supply of crvUSD in the market.</p><p>These actions are defined in the PegKeeper contract and can be called by any external caller. Calling PegKeeper to anchor crvUSD creates profits, and these profits are returned to the external caller in the form of LP tokens. For example, suppose PegKeeper initially mints 200 crvUSD, deposits it into the Curve pool, and obtains 300 LP tokens. When the price drops, only 200 LP tokens are needed to withdraw 200 crvUSD. The remaining 100 LP tokens will be owned by the external caller.</p><p>Although this mentions forming a pool with crvUSD in each Curve pool, it is more likely that the approach will be similar to the 3pool type of pool where crvUSD is pooled with other assets. This way, only one PegKeeper is needed to adjust the price of crvUSD. In addition, the Curve team can use CRV to vote on the crvUSD ↔ 3pool pool to increase rewards and incentivize people to deposit and mint crvUSD. However, these are just personal speculations, and we need to see the actual situation after launch.</p><h3 id="h-monetary-policy" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Monetary Policy</h3><p>What if the PegKeeper has burned all the uncollateralized crvUSD and the price of crvUSD is still below 1 USD? Curve Finance has another way to increase the crvUSD price, which is to use Monetary Policy to raise the borrowing rate.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/403bb86437d875ee0ce1c011a4ea46eedf0bd95cf1ca6f1bfa93de9ca0f07371.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><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8078d58385437d4d00f40d2d2a94e8d5f2f1b6d0cffdd001da8ed2f837a58615.png" alt="https://www.desmos.com/calculator/cqtavnvhps?lang=en" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">https://www.desmos.com/calculator/cqtavnvhps?lang=en</figcaption></figure><p>As we can see, when the price is greater than 1, the borrowing rate is extremely small (tending to 0). When the price is less than 1, the borrowing rate increases rapidly. This will cause borrowers who have minted crvUSD to repay their debt quickly, otherwise their collateral may be liquidated. It is noticed that the price affects the borrowing rate, and it also affects the amount of borrowing. Of course, the actual rate at which the borrowing rate increases will only be known after the crvUSD is officially launched.</p><hr><h2 id="h-tldr" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">TL;DR</h2><ul><li><p>LLAMMA is the algorithm used by Curve to perform collateral liquidation, which - reduces losses during liquidation by distributing collateral across different price ranges.</p></li><li><p>It generates arbitrage opportunities by using larger amplitude of price changes than external prices to dynamically liquidate collateral. When prices drop, collateral becomes crvUSD, and when prices rise, it becomes collateral again.</p></li><li><p>PegKeeper mints or burns uncollateralized crvUSD to decrease or increase the crvUSD price, while Monetary Policy controls borrowing rates to anchor crvUSD at 1 USD.</p></li></ul><p>The design of Curve Finance&apos;s mechanism is very sophisticated and is worth further study in terms of its implementation. When understanding the stablecoin mechanism of Curve, it is found that PACO has a deep understanding of studying Curve stablecoin. His articles and videos have been very helpful. The charts used in this article are from PACO&apos;s data, and it is highly recommended to read PACO&apos;s articles and videos for a deeper understanding. These references will be provided.</p><p>This article represents my own understanding and summary of researching Curve stablecoins, and I hope it can help readers gain some understanding of the mechanism. If there are any errors in the article, please kindly point them out and provide corrections. Discussions and exchanges of opinions are also welcomed. Finally, I would like to thank <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/r?url=https%3A%2F%2Fcyanho.medium.com%2F%3Fsource%3Duser_profile-------------------------------------">Cyan</a> Ho for reviewing this article and providing some ideas for improvement.</p><hr><p>Reference</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/curvefi/curve-stablecoin/blob/master/doc/curve-stablecoin.pdf">curve-stablecoin whitepaper</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://lundao.tech/blog/crvusd">LLAMMA - crvUSD 的新清算機制</a></p></li><li><br></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://curve.substack.com/p/november-22-2022-crvusd-white-paper?r=br6us&amp;utm_campaign=11-22">November 22, 2022: $crvUSD White Paper</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://crvusd.0xreviews.xyz/">crvUSD simulator</a></p></li><li><br></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/danrobinson/status/1595090420740112385?s=20">Path-Dependence</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://paco0x.org/curve-stablecoin/">https://paco0x.org/curve-stablecoin/</a></p></li></ul>]]></content:encoded>
            <author>albertlin@newsletter.paragraph.com (Albert.Lin)</author>
        </item>
        <item>
            <title><![CDATA[Zerion 如何利用 Genesis Card NFT 實現跨鏈 (on Polygon) 減免手續費]]></title>
            <link>https://paragraph.com/@albertlin/zerion-genesis-card-nft-on-polygon</link>
            <guid>KTo0nKUpClbAqOhU7XkF</guid>
            <pubDate>Wed, 21 Sep 2022 10:01:20 GMT</pubDate>
            <description><![CDATA[TLDRZerion 是利用鏈下判斷該帳戶是否有 Genesis NFT。若確認有擁有 Genesis NFT，Zerion的 Signer 去簽署一筆 fee = 0 的簽章並放在交易中。當交易送到合約的時候會去判斷簽章是否為 Zerion Signer 所簽。若為 Zerion Signer 所簽就不收費。Trace Flow在呼叫 Zerion swap token 的時候會去呼叫 Zerion Router 的execute() external function。因為我們想要知道 protocol fee 在什麼時候被收取，所以我們直接跳到最後 return execute(input, output, swapDescription) 這個 internal function 去看發生什麼事。在 execute() internal function 中，我們往下走在 #196 行會發現這一行程式碼 Base.transfer() 。這行程式碼就是把 fee 傳送到 Zerion MultiSig 中。fee 的數量就是 protocolFeeAmount 所記錄的數...]]></description>
            <content:encoded><![CDATA[<h2 id="h-tldr" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">TLDR</h2><blockquote><p>Zerion 是利用鏈下判斷該帳戶是否有 Genesis NFT。若確認有擁有 Genesis NFT，Zerion的 Signer 去簽署一筆 fee = 0 的簽章並放在交易中。當交易送到合約的時候會去判斷簽章是否為 Zerion Signer 所簽。若為 Zerion Signer 所簽就不收費。</p></blockquote><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ef46f35eaf3eca7f17b9396588e1ed93f599cee2c47bf81a5a5efb014f4b1225.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><h2 id="h-trace-flow" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Trace Flow</h2><p>在呼叫 Zerion swap token 的時候會去呼叫 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://polygonscan.deth.net/address/0xd7F1Dd5D49206349CaE8b585fcB0Ce3D96f1696F#code">Zerion Router</a> 的<code>execute()</code> external function。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/60cecb40a345fc4fc2cdb7e9ee1447dc47713251b5307811b714d79ce1723a23.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><p>因為我們想要知道 protocol fee 在什麼時候被收取，所以我們直接跳到最後 <code>return execute(input, output, swapDescription)</code> 這個 internal function 去看發生什麼事。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2e4b00b81895a596483dca31b7f4d0fd7181fabc37d73a6c79c06522fb93734e.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><p>在 <code>execute()</code> internal function 中，我們往下走在 <code>#196</code> 行會發現這一行程式碼 <code>Base.transfer()</code> 。這行程式碼就是把 fee 傳送到 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://polygonscan.com/address/0x4a183b7ed67b9e14b3f45abfb2cf44ed22c29e54#code">Zerion MultiSig</a> 中。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5dd537c0d10dbd3cacb2c9355a86f7b7e86456e015f4e9ac91686ef3b8e5402c.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><p>fee 的數量就是 <code>protocolFeeAmount</code> 所記錄的數值。哪這個數值是在哪邊決定的呢？往上面的程式碼找尋可以發現 <code>protocolFeeAmount</code> 是從 <code>getReturnedAmounts()</code> 所決定。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a3ae0bbef5c4be82b0bd04c8f69bef5d424f86e5acd0d85fa56ef789b5b958b2.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><p>在 <code>getReturnedAmounts()</code> 的 <code>#496</code> 是決定 <code>protocolFeeAmount</code> 的數字怎麼來。可以發現它是由 <code>totalFeeAmount * protocolFee.share</code> 所決定。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f3d6c02eb46d985c318069eb7930c32104e8b6753ed54d02b19d722c1accb2ee.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><p>我們知道若 Sender 持有 Genesis Card NFT 可以擁有 Zero Fee 的減免。<code>protocolFeeAmount</code> 變成 0 條件有兩個。 <code>totalFeeAmount</code> 等於 0 或<code>protocolFee.share</code> 等於 0 （或同時為 0 ）。 於是我找了一個擁有 Genesis Card NFT 且 fee 為 0 的 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://dashboard.tenderly.co/tx/polygon/0x2b22b61d66703424fe1f3230b753c17e46c714429ea728636d7400e21f147687/debugger?trace=0">transaction</a> 來一探究竟。 使用 <code>Tenderly</code> 發現他的 <code>protoclFee.share</code> 就是帶 0。由此判斷是一開始帶進來的參數就指定了 <code>protocolFee.share</code>來決定這次 protocol fee 要收多少。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/97f0d2a623ce3c10616832814f9ee20efd6cacd98d6998009bfe1463cb677679.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><p>此時出現一個疑問。若 protocol fee 是由參數帶進來，那我們不就可以任意決定 protocol fee 了嗎？這時候讓我們回頭看到一開始的 <code>execute()</code> extneral function。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/60cecb40a345fc4fc2cdb7e9ee1447dc47713251b5307811b714d79ce1723a23.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><p>裡面跟 Fee 有關的檢查是 <code>validateProtocolFeeSignature()</code> 這一個 function。接下來我們就來分析裡面做了什麼事情。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5d787146c6ec8e3cee3bdf652759d0e015b500c736a178ea2dc0cebd59a37625.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><p>第一個部分是去檢查這次交易有沒有包含 <code>protocolFeeSignature</code>。若沒有（length會等於0）就單純檢查收 fee 的對象和 <code>protocolFee.share</code> 是否跟 contract 預設的一樣。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/98234dfbcd508829d70e88ea67e7704917d1996c0ee66d3e6722db4bc99d62da.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><p>若有帶 <code>protocolFeeSignature</code> ，就去呼叫 <code>hashProtocolFeeSignatureData()</code> 取得 signatureData。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/456385c670f0b51b51d10d2b3f92219e3e22ee3a77bad05fc524b4d4d6c15d9a.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><p><code>hashProtoclFeeSignatureData</code> 就是把部分參數包含 <code>swapDescription</code> 和 <code>dealine</code>等取 hash 值。這個 <code>hash</code> 就是 Signature 所簽的 data。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ba10aa2130bbdcef143412224cc36b859e1ca295f21b0e85fb42091a74ced4c9.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><p>得到了 signature data 之後就去呼叫 <code>isValidSignatureNow()</code> 來比對簽章是否正確。可以注意 <code>isValidSignatureNow()</code> 其中一個參數是 <code>getProtoclFeeSigner()</code> ，這個是 Zerion Router 合約裡面所定義的 signer。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/081284dd73922a4ab9829243b0e9112b4a5e6aedc7391ec0928604436f5ca036.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><p><code>isValidSignatureNow()</code> 裡面就做兩件事情。</p><ol><li><p>利用剛剛得到的 <code>hash</code> 和 signature 來還原簽署的 address (就是裡面的 <code>recovered</code>)。比對 <code>recovered</code> 和 <code>signer</code> 是否相同。若相同就代表這是由 Zerion signer 所簽署，確認 <code>swapDescription</code> 裡面所帶的 <code>protocolFee</code> 相關資訊是被 Zerion Signer 所認可。</p></li><li><p>下方的 <code>staticall</code> 是支援若 Zerion Signer 是合約而非單純的 EOA。因為合約沒有 key，所以無法簽署簽章。所以利用 ERC1271 的方式來實現合約簽章。詳細內容可以參考這篇<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/taipei-ethereum-meetup/eip-1271-%E8%AE%93%E4%BD%A0%E7%9A%84%E5%90%88%E7%B4%84%E4%B9%9F%E5%8F%AF%E4%BB%A5%E7%B0%BD%E5%90%8D-dc4e1c9c84a0">文章</a>。</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d5fb58da0c3295a39732bb8a307817e524f2635eaf478ce2022edb96175dd053.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><p>若確認簽章無誤，最後就檢查是否超過簽署的 dealine。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5b95e2a6cc79404f8e44e69f4700bc5c3f49f19c630f30358205f86a3bbf7343.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><p>通過 deadline 的檢查後，就會依照 <code>protocolFee</code> 所記錄的資訊（包含 reciver 和 fee amount）去收取 protocol fee。</p><h2 id="h-takeaway" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Takeaway</h2><p>因為目前跨鏈的機制還尚未成熟，所以 Zerion 利用一個 signer 的角色來判斷該筆交易是否需要收 protocol fee。這樣的機制或許不是那麼去中心化，但卻是一個比較合理的做法。這樣的方式可以快速運用在不同的鏈上，而不需要考慮鏈與鏈之前該如何溝通。使用者也只需要在某個鏈上擁有該 NFT 就好，不需要每個鏈都有。缺點仍是中心化，若之後 Zerion 想要修改規則，變成改收 1% 的 protocol fee，一般使用者也只能乖乖接受（Zerion Signer 可以簽他們想要簽的任何交易）。在過渡時間這尚可是一個可以接受的方式，期待未來跨鏈機制的成熟，就不需要利用這個中心化的方式來做，而可以真的直接讀取鏈上的資訊來收費。</p>]]></content:encoded>
            <author>albertlin@newsletter.paragraph.com (Albert.Lin)</author>
        </item>
        <item>
            <title><![CDATA[How ERC721Psi Work]]></title>
            <link>https://paragraph.com/@albertlin/how-erc721psi-work</link>
            <guid>VMEYer8yGyj06ognixAT</guid>
            <pubDate>Sat, 25 Jun 2022 15:58:19 GMT</pubDate>
            <description><![CDATA[How ERC721Psi Work最近剛好看到有人介紹 ERC721 相關的變形。其中 ERC721Psi 又是其中針對 降低 gas cost 最有效率的一個，於是興起了研究的想法。這邊跟大家介紹一下 ERC721Psi 主要改進了什麼和用什麼方式改進Introduce ERC721ERC721 是目前主流的 NFT 的格式之一（另外還有 ERC1155)。大家對於 NFT 的需求增高，隨之而來的就是成本的降低。各種 ERC721 的變形也就孕育而生。介紹一下 ERC721 的一些概念，這些概念有助於理解上ERC721Psi 是如何減少 gas cost。在 ERC721 中每一個 token 對應一個 NFT，每一個 token 都有自己的 tokenId 和 owner 。在 ERC721 合約中會有一個 _owners 的 map 來記錄 tokenId 對應的 owner 。當你在傳送 token 給其他人的時候，就會去修改 _owners 該 token 的 owner。當你同時 mint 多個 NFTs 的時候，你需要對這些 NFTs 在 _owners 對應的位...]]></description>
            <content:encoded><![CDATA[<h1 id="h-how-erc721psi-work" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How ERC721Psi Work</h1><p>最近剛好看到有人介紹 ERC721 相關的變形。其中 ERC721Psi 又是其中針對 降低 gas cost 最有效率的一個，於是興起了研究的想法。這邊跟大家介紹一下 ERC721Psi 主要改進了什麼和用什麼方式改進</p><h1 id="h-introduce-erc721" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduce ERC721</h1><p>ERC721 是目前主流的 NFT 的格式之一（另外還有 ERC1155)。大家對於 NFT 的需求增高，隨之而來的就是成本的降低。各種 ERC721 的變形也就孕育而生。介紹一下 ERC721 的一些概念，這些概念有助於理解上ERC721Psi 是如何減少 gas cost。在 ERC721 中每一個 token 對應一個 NFT，每一個 token 都有自己的 <code>tokenId</code> 和 <code>owner</code> 。在 ERC721 合約中會有一個 <code>_owners</code> 的 map 來記錄 <code>tokenId</code> 對應的 <code>owner</code> 。當你在傳送 token 給其他人的時候，就會去修改 <code>_owners</code> 該 token 的 <code>owner</code>。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c00300698881b4a01aadae9bdb2323c417acfa8ce7b89f2b06b6862e874f6f76.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><p>當你同時 mint 多個 NFTs 的時候，你需要對這些 NFTs 在 <code>_owners</code> 對應的位置寫上你的 address 來表示你是這些 NFTs 的 <code>owner</code>。這也導致在 mint 多個 NFTs 時所需要花費的 gas 會是 mint 單一個 NFT 時的複數倍。那有什麼方式可以減少 gas cost 嗎？</p><h1 id="h-erc721a" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">ERC721A</h1><p>ERC721A 是由著名的 NFT Project — <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.azuki.com/">Azuki</a> 開發團隊所發表。其中針對於 mint 多個 NFT 所花費的 gas cost 進行了改善。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/255c27d6b90abb47097dca77a6631d836ed885c6da81908c5f90c751a6ffc259.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><p>其原理我用一個範例來解釋。假設今天我 (Albert) 一口氣去 mint 第50號～第54號的 NFT (50,51,52,53,54)。如果是原本 ERC721 的做法，在這些 NFTs 都會寫上 owner 是 Albert。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2bc85a0dd6e20b6638f22be25e9107cca878a54b2e1aff1f36ccb7046cf39121.png" alt="ERC721" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">ERC721</figcaption></figure><p>若是 ERC721A 的做法會變成，只有第一個 (#50) 的 NFT 會填上 owner = Albert 。其他的 NFTs (#51~#54) 都會是 <code>owner_not_set</code> 的狀態。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a0c3f32a27f0f936a69b20d511b66b8f809243cccd7e2dc9ba9bfaa8db9c624e.png" alt="ERC721A" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">ERC721A</figcaption></figure><p>若我們今天想要把 #53 NFT 轉移給 Bob 時，要如何知道 #53 NFT 的 owner 就是 Albert 呢？此時我們只要從 #53 NFT 往前找，找到第一個有 owner 的 NFT，該 owner 就是 #53 NFT 的 owner。此時只要把 #53 NFT 的 owner 填上 Bob ，並把 #54 NFT 的 owner 填上 Albert 就可以了。若沒有把 #54 NFT的 owner 也一併填上 Albert 話，會導致連 #54 NFT 都是 Bob 擁有。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fbfb427c830e4c06b867a6f5fcadff29f5ac1ac8fcb8ddb4f5dcae2b7275a3d9.png" alt="transfer #53 to Bob" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">transfer #53 to Bob</figcaption></figure><p>可以從上圖發現從 #53 NFT 要確認 owner 是 Albert 需要往前找三個 token 才知道 owner 是 Albert。那有什麼方式可以快速知道這件事嗎？這個就是 ERC721Psi 想要改善的問題。</p><h1 id="h-erc721psi" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">ERC721Psi</h1><p>ERC721Psi 利用神奇的演算法有辦法快速定位距離 #53 最近有 owner 的 NFT 是哪一個。在解說之前有一些觀念需要先跟大家解說一下，方便等一下知道是如何運用在 ERC721psi 中。</p><h2 id="h-bitmap" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Bitmap</h2><p>因為存在電腦中的資料都是以二進位方式儲存，二進位的特性來記錄和運算資料。其中每一個 bit 我們都個別可以拿來記錄資料。舉例現在有三個 NFT (#1, #2 和 #3），我們可以使用一個 uint8 的變數和搭配 bitmap 的方式來記錄他們有沒有 被 mint。 因為<code>uint8</code> 變數以二進位表示剛好有 3 bit ( <code>000</code> )。當每一個 bit 為 <code>0</code> 的時候我們可以視為沒有被 mint，當 bit 為 <code>1</code> 可以視為已經被 mint 出來了。上面那張圖表示 #1 NFT 已經被 mint 出來了，#2 和 #3 還沒有，此時 <code>uint8</code>變數值為 4 (十進位）。下面那張圖的 <code>uint8</code>變數值為 5 （十進位），表示 #1 和 #3 皆都 mint 出來，唯獨 #2 還沒有。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/67a1c739807b27db160c13ee5056cd9042d772f11ffbd3a72c06e54e9bc4bc5c.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><h2 id="h-de-bruijn-sequence" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">De Bruijn Sequence</h2><p>de Bruijn sequence 是指有某種特性的數列。當這個數列是由 k 種字母組成，給定長度為 n 的連續子數列，總長度為 k^n 。每一種子數列裡面的組合皆不一樣，這種數列我們就稱之為 de Bruijn sequence （標示成 B(k, n)）。舉例來說，我們設定 k= 2 ，字母就選 0 和 1 表示。n = 3：代表連續子數列長為 3。整個數列長度會是 2³ = 8。 數列 <code>00010111</code>就剛好是符合這些條件的其中一種 de Brujin Sequence。由下圖可以發現每個 sub-sequence 都是不同的排列組合。若把他們轉成數字會發現每個 sub-sequence 都會對應一個唯一數字。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/58779adc98aea7c406c1ea862406fa25e9a22e31bce37737680d726dbc73faed.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><h1 id="h-how-to-index-the-owner-of-token-id" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How to index the owner of token Id</h1><p>用一個例子來說明在 ERC721Psi 是怎麼利用上面兩個技術來達成快速找到 owner 。在 contract 裡面是用 256 bitmap 來記錄 owner。為了大家理解方便，我們這邊還是使用 8 bitmap 來說明。 主要的 function 是 <code>BitMaps.sol</code> 的 <code>scanForward(uint256 index)</code> ( <code>index</code> 是指 tokenId)。</p><h2 id="h-use-bitmap-to-record-the-existement-of-the-owner-of-tokenid" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Use Bitmap to record the existement of the owner of tokenId</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2a1dedde280190f35f003cf41a306e4e6ee4d8b9a0d1fabd07d5f88bc2a92cb3.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><p><code>uint256 bucketIndex = (index &amp; off)</code> 把 index 除與 256 來獲得此 tokenId 會是放在哪一個 bucket。一個 bucket 是以 256 為一個單位（表示存放了 256 tokenId 的 owner)。 <code>uint256 bucketIndex = (index &amp; 0xff)</code> 計算出在該 bucket 中這個 tokenId 對應的位置。 <code>bb = bb &gt;&gt; (0xff ^ bucketIndex)</code> 是將要查詢的 tokenId 對應的 bitmap 移到最右邊。情形會如同下圖所示。Batch Head 就是我們要找該 tokenId 的 owner</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/89ca3f78cdd0a66ea9ecbfe0e099856b6673f0d05fdc692cccdc4929a220e8e6.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><h2 id="h-find-ls1b" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Find LS1B</h2><p>我們希望 tokenId 往左邊第一個 bit 為 1 的位置留下來，其他都設成 0 。這邊使用了一個小技巧，如 <code>isolateLS1B256(uint256)</code> 所示。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8b5dd3a2bca7c2a52a4cb08004e8d330223936c63b08a0ff04c1096ff77cbcb9.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><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a8b173b3ec7203176e2f7863a96dd0461d06e22d06817a25167c533293202f4f.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><p>呈現的效果就會如同下圖所示，除了從右邊數來第一個 1 的位置是 1 之外，其他都為 0 。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b505aedb695e52de0c6e493d98152bcc6472e58fba71f186f356961532b16130.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><p>圖中可以發現距離 <code>1</code> 的位置有三個間隔，所以表示我們需要往右移動三次。那有什麼方式可以不用移動三次就可以知道 <code>1</code> 的位置呢？這就要用到 <strong>De Bruijn Sequence</strong></p><h2 id="h-de-bruijn-sequence-ls1b" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">利用 De Bruijn Sequence 快速找出與 LS1B 的距離</h2><p>De Bruijn Sequence 就以前面的 <code>000101111</code> 做範例。我們現在已經知道 LS1B ( <code>00001000</code>) 是在右邊數過來第四個位置。此時把 LS1B ( <code>00001000</code>) 和 De Bruijn Sequence(<code>000101111</code> ) 做相乘，等同於跟把 De Bruijn Sequence(<code>000101111</code> ) 往左移 4 個 bit。之後再把得到的結果往右移 <code>k^n-n</code> 位數（範例 <code>k=2, n=3, 2^3-3 = 5</code>)。可得到對應的且唯一的 sub-sequence( <code>101</code>)。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2572f20c44e7ca051641cc523f207a60e70816191df3d8f1996c7717ac799882.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><p>因為唯一 sub-sequence 會對應一個唯一數字。我們可以針對這些數字建立一個 map (or lookup table)。key 為 sub-sequence number， value 為與 LS1B 之間的距離。這樣就可以直接快速定位出與 LS1B 的距離而不需要利用輪詢的方式找出。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1e254140834716edab52a17aa417b994095d050bf36279b869e20a96d4b3cbba.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><h1 id="h-gas-cost" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Gas Cost</h1><p>可以發現不管是在執行 mint 或者做 transfer 所花費的 gas 都有顯著的減少。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8b8304a55421a52bd4b733485f57a8bf4222efc438c761a2c5ffd26a1aabe3e7.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><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3bba80ec866a7c7192d87ca5511d6303f036c87b10a1d69922912c56e8e98cba.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><h1 id="h-takeaway" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Takeaway</h1><p>以上就是主要 ERC721Psi 用來減少 gas cost 主要用到的技術。熱門的 project所消耗的 gas 可以說是非常驚人。所以現階段在開發 dApp 時候，gas cost 也會成為主要考量之一。ERC721 Psi 開發者使用的手段非常精妙，非常值得學習。但因為 ERC721Psi 尚未經過市場驗證，是否能保證一定安全無虞，還需要時間的考驗。隨著 L2 或其他 L1 公鏈的興起，gas cost 有機會可以大幅下降。屆時 dDapp 的架構會可以進一步更加複雜，能實現的更多的功能！</p><h1 id="h-reference" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reference</h1><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.slideshare.net/EstarriolVetch/erc721psi">ERC721PSI</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/estarriolvetch/ERC721Psi">ERC721 Github</a></p></li></ul>]]></content:encoded>
            <author>albertlin@newsletter.paragraph.com (Albert.Lin)</author>
        </item>
        <item>
            <title><![CDATA[Introduce Yearn Aollowlist]]></title>
            <link>https://paragraph.com/@albertlin/introduce-yearn-aollowlist</link>
            <guid>hjEsw7vOMzNxIfDHHCSe</guid>
            <pubDate>Wed, 11 May 2022 03:50:46 GMT</pubDate>
            <description><![CDATA[Yearn announced Yearn Allowlist on May 6, 2022, in order to improve product security. As a result, I conducted some research to see how Allowlists might improve product security.ScenariosMan-in-the-middle attacks are usually avoided by using this method. For example, to retrieve txdata, load it into a transaction, and send it out, the user will frequently utilize the paraswap API. The user&apos;s assets may be lost if the returned txdata is maliciously manipulated by an intermediary hacker. Y...]]></description>
            <content:encoded><![CDATA[<p>Yearn announced Yearn Allowlist on May 6, 2022, in order to improve product security. As a result, I conducted some research to see how Allowlists might improve product security.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/429e53bbbc56c82ecbb2049a4d4509faaf0844c04dba91cf45fa22d067b49bf9.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><h2 id="h-scenarios" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Scenarios</h2><p>Man-in-the-middle attacks are usually avoided by using this method. For example, to retrieve <code>txdata</code>, load it into a transaction, and send it out, the user will frequently utilize the paraswap API. The user&apos;s assets may be lost if the returned <code>txdata</code> is maliciously manipulated by an intermediary hacker. You may now use paraswap&apos;s allowlist to check whether the returned <code>txdata</code> is accurate. (This is just an example; I&apos;m not sure whether or not paraswap uses allowlist.)</p><p>Allowlist defines a schema that other people can use to make their own allowlists. Multiple conditions make up the allowlist. When <code>txdata</code> meets one of the conditions, it is valid.</p><h1 id="h-flow" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Flow</h1><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/759840f4aafa1d62a7e6bfe31e96e643dcfaa0e4da0b0e9153142ffca3d1be26.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><h1 id="h-condition" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Condition</h1><p>A condition refers to a function and provides an implementation contract. A condition defines the data sequence (including checking function, implementation id...) The format of each condition is depicted in the diagram below.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3dea49cfcb21c86e2eb42c4903b763b18b53ec0ab8419d2b91f6487c9385793e.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><ul><li><p><code>id</code>: condition id</p></li><li><p><code>implementationId</code>: An implementation contract address is represented by an id. The imlementationById map stored in the allowlist contract corresponds to the id and contract address.</p></li><li><p><code>methodName</code>: The name of the target contract&apos;s method to invoke.</p></li><li><p><code>paramTypes</code>: The types of the function arguments.</p></li><li><p><code>requirements</code> : There are two parts (type and verify function). <code>target</code> (check target contract) and <code>params</code> are the two types (check function parameters). The verification function specifies which implementation function should be used for checking.</p></li></ul><p><code>ValidateCalldataByAllowlist()</code> checks every condition registered in Allowlist. <code>testCondition()</code> will go over the conditions that were previously registered in Allowlist.</p><h2 id="h-verify-the-order" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Verify the order</h2><h3 id="h-check-function-selector" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Check Function Selector</h3><p>Calculate the function selector using the <code>methodName</code> and <code>paramTypes</code> of the condition to compare the first four bytes of <code>txdata</code>.</p><h3 id="h-check-target-address" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Check Target Address</h3><p><code>CheckTarget()</code> is called if the requirement type is <code>target</code>. To generate <code>txdata</code>, use <code>methodSignature</code> and <code>targetAddress</code>, and call the implementation&apos;s verify function with staticCall.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2bfa6b181ff6ec4e3ce55de90d9b3c4f8ddad88d9d2bb31b0565aee2fbd5c276.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><h3 id="h-check-function-parameters" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Check function parameters</h3><p>If the requirement type is <code>params</code>, <code>CheckParams()</code> is called. To acquire the parameter values, use <code>AbiDecoder.getParamFromCalldata()</code> with <code>data</code> and <code>paramsTypes</code>.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/091720bc1752dbafa5419541eaeecce9ca10b7f1dfaaf9a158049f0d9435393d.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><p>To make a <code>txdata</code>, combine the verify function (<code>methodSignature</code>) with the value of the params just solved. To call the implementation&apos;s verification function, use staticCall.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8e0908c5837c3a93c9e2948a420fac9f0c4cdd6ed67c8642017eaa15504d87fb.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><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c168f5a3a4a93294cef13f515a4fe9b6fe77f8055313a32c2280fde109d5e96a.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><h2 id="h-connect-allowlist-to-website" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Connect Allowlist to website</h2><p>It is advised that you utilize ENS/DNSSEC to validate the Allowlist&apos;s authenticity.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4266af50fb2941afa76139d720b7759a916f13d7854003f23db10a5d4e167221.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><h1 id="h-personal-thoughts" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Personal Thoughts</h1><p>DApps are becoming increasingly complicated, and performing all computations on-chain will undoubtedly consume a significant amount of gas. Off-chain APIs may become a viable option in the future, but they raise additional security risks. Allowlist is an attempt to reconcile off-chain and on-chain data inconsistencies. To reduce risks and prevent the loss of user assets caused by malicious attacks, it is suggested that all services that provide APIs develop an Allowlist method.</p><h1 id="h-reference" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reference</h1><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/iearn/yearn-allowlist-71757d4e3cf4">Yearn Allowlist</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/yearn/yearn-allowlist/blob/main/tests/test_vault_conditions.py">yearn-allowlist</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/yearn/eth-allowlist/tree/03f2a9ad5716abd0dbfc6d45885f5d6a04061edc">yearn/eth-allowlist</a></p></li></ul>]]></content:encoded>
            <author>albertlin@newsletter.paragraph.com (Albert.Lin)</author>
        </item>
    </channel>
</rss>