<?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>0xBitFly</title>
        <link>https://paragraph.com/@0xbitfly</link>
        <description>undefined</description>
        <lastBuildDate>Sun, 14 Jun 2026 08:09:43 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>0xBitFly</title>
            <url>https://storage.googleapis.com/papyrus_images/1cbb97ac15aa6e312e98424bd23485dd975ca73c419abaf7fdcc7ad193b1bf4e.jpg</url>
            <link>https://paragraph.com/@0xbitfly</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[账户抽象之路#4-聚合签名]]></title>
            <link>https://paragraph.com/@0xbitfly/4</link>
            <guid>MNfO4eyKsdJYN6zE73PM</guid>
            <pubDate>Mon, 03 Apr 2023 11:56:24 GMT</pubDate>
            <description><![CDATA[聚合签名我们当前的实现方案，是分别验证捆绑包中的每个用户操作，这是一种非常直接的验证方式，但会造成gas的浪费，检查签名最终可能会在 gas-wise 方面变得昂贵，因为这样做需要运行相当多的加密算法运算。 如果我们可以只用一个签名，而不是多个签名同时验证许多操作，那不是很好吗？ 这样做取决于密码学中的一个概念，聚合签名。 支持聚合的签名方案提供了一种方法，给定多个使用不同密钥签名的消息，然后生成单个组合签名，验证这个组合签名，如果组合签名验证通过，那么下面所有的单个签名也都是合法的。（译注：有点类似于merkle tree，减少链上存储体积，这里是减少签名验证步骤） 常见的支持聚合签名的方案是 BLS。 这种优化对于实现卷叠(Rollup)特别有用，因为rollup的主要目的是数据压缩，而签名聚合让我们可以压缩签名部分。 有关签名聚合节省空间的更多信息，请参阅 Vitalik 关于该主题的推文。引入聚合我们立即看到，并非捆绑包中的所有用户操作都可以将其签名汇总在一起。请记住，钱包被允许使用它想要的任何逻辑来验证其给定的签名，因此同一捆绑包中可能存在各种签名方案。 由于我们可能无...]]></description>
            <content:encoded><![CDATA[<h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">聚合签名</h2><p>我们当前的实现方案，是分别验证捆绑包中的每个用户操作，这是一种非常直接的验证方式，但会造成gas的浪费，检查签名最终可能会在 gas-wise 方面变得昂贵，因为这样做需要运行相当多的加密算法运算。</p><p>如果我们可以只用一个签名，而不是多个签名同时验证许多操作，那不是很好吗？</p><p>这样做取决于密码学中的一个概念，聚合签名。</p><p>支持聚合的签名方案提供了一种方法，给定多个使用不同密钥签名的消息，然后生成单个组合签名，验证这个组合签名，如果组合签名验证通过，那么下面所有的单个签名也都是合法的。（译注：有点类似于merkle tree，减少链上存储体积，这里是减少签名验证步骤）</p><p>常见的支持聚合签名的方案是 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/BLS_digital_signature">BLS</a>。</p><p>这种优化对于实现卷叠(Rollup)特别有用，因为rollup的主要目的是数据压缩，而签名聚合让我们可以压缩签名部分。</p><p>有关签名聚合节省空间的更多信息，请参阅 Vitalik 关于该主题的<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/VitalikButerin/status/1554983955182809088">推文</a>。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">引入聚合</h2><p>我们立即看到，并非捆绑包中的所有用户操作都可以将其签名汇总在一起。请记住，钱包被允许使用它想要的任何逻辑来验证其给定的签名，因此同一捆绑包中可能存在各种签名方案。</p><p>由于我们可能无法聚合来自不同方案的签名，我们的捆绑包最终将产生N组操作，每个组使用不同的聚合方案或根本没有聚合方案。</p><p>由于我们需要在链上表示各种聚合方案，每个方案都有自己的逻辑，因此我们将让每个聚合方案用一个合约表示，我们将其称为<strong>聚合器</strong>。</p><p>一个聚合方案的定义是它如何将多个签名组合成一个，以及如何验证组合签名，因此聚合器有以下两个接口：</p><pre data-type="codeBlock" text="contract Aggregator {
  function aggregateSignatures(UserOperation[] ops)
    returns (bytes aggregatedSignature);

  function validateSignatures(UserOperation[] ops, bytes signature);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Aggregator</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">aggregateSignatures</span>(<span class="hljs-params">UserOperation[] ops</span>)
    <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes</span> aggregatedSignature</span>)</span>;

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateSignatures</span>(<span class="hljs-params">UserOperation[] ops, <span class="hljs-keyword">bytes</span> signature</span>)</span>;
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4518f1ff2441f021fe89e1d7adaf4c10df46fbbc5fef985d72f9cb1f92a99d7c.jpg" alt="聚合器合约将多个用户的操作合并到一个具有单个签名的组合中" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">聚合器合约将多个用户的操作合并到一个具有单个签名的组合中</figcaption></figure><p>由于每个钱包都有定义自己的签名方案，因此由每个钱包来决定它与哪个聚合器兼容（如果有的话）。</p><p>如果钱包想要参与聚合，它会提供一种获取其聚合器的方法</p><pre data-type="codeBlock" text="contract Wallet {
  // ...

  function getAggregator() returns (address);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Wallet</span> </span>{
  <span class="hljs-comment">// ...</span>

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAggregator</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>)</span>;
}
</code></pre><p>使用这种新的getAggregator方法，打包者可以将具有相同聚合器的操作分组在一起，并使用该聚合器的aggregatorSignatures方法为它们计算组合签名。</p><p>一个组合看起来像这样：</p><pre data-type="codeBlock" text="code
struct UserOpsPerAggregator {
  UserOperation[] ops;
  address aggregator;
  bytes combinedSignature;
}
"><code>code
struct UserOpsPerAggregator {
  UserOperation<span class="hljs-section">[]</span> ops<span class="hljs-comment">;</span>
  address aggregator<span class="hljs-comment">;</span>
  bytes combinedSignature<span class="hljs-comment">;</span>
}
</code></pre><blockquote><p>如果打包者具有某一特定聚合器的链下知识（译注：源码？），则可以通过硬编码本机版本的签名聚合算法，来优化这一操作，而不是运行aggregateSignatures的EVM代码。</p></blockquote><p>接下来，我们需要更新入口点合约，才能使用新的聚合器。</p><p>回想一下，入口点有一个<code>handleOps</code>方法，它接收一个ops的列表作为输入参数。</p><p>我们将给它一个新方法，<code>handleAggregatedOps</code>，它做同样的事情，但接受的是按聚合器分组后的操作：</p><pre data-type="codeBlock" text="contract EntryPoint {
  function handleOps(UserOperation[] ops);

    function handleAggregatedOps(UserOpsPerAggregator[] ops);

  // ...
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EntryPoint</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleOps</span>(<span class="hljs-params">UserOperation[] ops</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleAggregatedOps</span>(<span class="hljs-params">UserOpsPerAggregator[] ops</span>)</span>;

  <span class="hljs-comment">// ...</span>
}
</code></pre><p>新方法<code>handleAggregatedOps</code>的工作原理与<code>handleOps</code>基本相同。唯一的区别在于它的验证步骤。</p><p>虽然<code>handleOps</code>通过调用每个钱包的<code>validateOp</code>方法来执行验证，但<code>handleAggregatedOps</code>将使用聚合器在每个组的组合签名上 调用聚合器的<code>validateSignatures</code>方法。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/189bd257e2be4255fa3997ab1bd0f769fa868f72b53a37685305cce8bc69a815.jpg" alt="执行者使用聚合器将用户操作分组，然后再将它们发送到入口点，因此它们都可以同时进行验证" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">执行者使用聚合器将用户操作分组，然后再将它们发送到入口点，因此它们都可以同时进行验证</figcaption></figure><p>我们快完成了！</p><p>但这里有一个已经很熟悉的问题。</p><p>打包者希望模拟验证，并在打包之前验证这些聚合器的签名是合法的，因为如果验证失败，打包者将被迫支付gas费用。但具有任意逻辑的聚合器在模拟过程中很容易成功，但在执行过程中失败。</p><p>我们用之前代付者和工厂合约完全相同的方式解决这个问题：我们会限制聚合器可以访问哪些存储以及它可以使用哪些操作码，并要求它在入口点合约质押ETH，除非它不访问存储。</p><p>这就是聚合签名！</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">热身</h2><p>我们在这里创建账户抽象是与ERC-4337的完整架构近乎一样的！只是细节上有一些差异，例如一些方法的名称和参数，但架构上几乎没什么大的差异了。如果我这篇文章解释的很好，你现在应该能够理解ERC-4337具体是什么样了。</p><p>如果你已经读到了这里了，非常感谢你阅读我这一版本的解释！我希望它能帮助你，就像它能帮助我一样。</p><h2 id="h-erc-4337" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">附录：与ERC-4337的差异</h2><p>虽然我们已经了解了帐户抽象的整体架构，但ERC-4337背后的聪明人想到了一些与我们上面描述略有不同的事情。</p><p>让我们来看看其中的一些！</p><h3 id="h-1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. 验证时间范围</h3><p>上图，我对钱包的<code>validateOp</code>和代付者的<code>validatePaymasterOp</code>的返回类型非常困惑。ERC-4337找到了利用这一点的好方法。</p><p>钱包非常想做的事情是，只允许用户操作在一定时间内有效。否则，恶意的打包者可以让用户操作停留很长时间，然后在更长时间后将其包含在对打包者有利的捆绑包中。</p><p>钱包可以通过在验证期间检查TIMESTAMP来防止这种情况，以确保这个操作不是停留了太长时间，但它行不通，因为我们在验证期间禁止了TIMESTAMP，以防止线下模拟不准确的情况，这意味着钱包需要另一种方式来指示操作在什么时间有效。</p><p>因此，ERC-4337给了<code>validateOp</code>一个返回值，钱包可以使用该值来选择有效的时间范围：</p><pre data-type="codeBlock" text="contract Wallet {
  function validateOp(UserOperation op, uint256 requiredPayment)
    returns (uint256 sigTimeRange);
  // ...
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Wallet</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateOp</span>(<span class="hljs-params">UserOperation op, <span class="hljs-keyword">uint256</span> requiredPayment</span>)
    <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> sigTimeRange</span>)</span>;
  <span class="hljs-comment">// ...</span>
}
</code></pre><p>此返回值表示验证有效的时间范围，用两个8字节整数表示。</p><p>ERC-4337的另一个注意事项：在验证失败的情况下，钱包应该从<code>validateOp</code>返回标识值（sentinel value），而不是回滚，这有助于估算gas费，因为<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.alchemy.com/reference/eth-estimategas">eth_estimateGas</a>不会告诉你在回滚的交易中使用了多少gas。</p><h3 id="h-2" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. 钱包合约和工厂合约任意数据调用</h3><p>我们的钱包接口是：</p><pre data-type="codeBlock" text="contract Wallet {
  function validateOp(UserOperation op, uint256 requiredPayment);
  function executeOp(UserOperation op);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Wallet</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateOp</span>(<span class="hljs-params">UserOperation op, <span class="hljs-keyword">uint256</span> requiredPayment</span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
}
</code></pre><p>在ERC-4337中，智能钱包实际上没有名为<code>executeOp</code>的方法。</p><p>相反，用户操作有一个<code>callData</code>字段：</p><pre data-type="codeBlock" text="struct UserOperation {
  // ...
  bytes callData;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">bytes</span> callData;
}
</code></pre><p>这作为调用数据传递给钱包。</p><p>对于典型的智能合约，此数据的前四字节将被解释为<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zhuanlan.zhihu.com/p/421087023">函数标识符</a>，其余字节将被解释为函数参数。</p><p>这意味着除了所需的<code>validateOp</code>方法外，钱包可以定义自己的接口，并且用户操作可用于调用钱包上的任意方法。</p><p>同样，在ERC-4337中，工厂合约实际上没有<code>deployContract</code>的方法，他们也接收任意的call数据，在这种情况下，是从操作的<code>initCode</code>字段接收。</p><h3 id="h-3" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. 代付者和工厂合约的压缩数据</h3><p>上面我们说过，用户操作包含指定代付者的字段，以及要传递给它的数据：</p><pre data-type="codeBlock" text="struct UserOperation {
  // ...
  address paymaster;
  bytes paymasterData;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">address</span> paymaster;
  <span class="hljs-keyword">bytes</span> paymasterData;
}
</code></pre><p>在ERC-4337中，这些被合并到一个字段中作为优化，其中字段的前20个字节是代付者地址，其余是数据：</p><pre data-type="codeBlock" text="struct UserOperation {
  // ...
  bytes paymasterAndData;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">bytes</span> paymasterAndData;
}
</code></pre><p>工厂合约也是如此，虽然我们使用两个字段<code>factory</code>和<code>factoryData</code>，但ERC-4337将它们组合成一个字段<code>initCode</code>。</p><p>好的，你做到了！</p><p>我们希望您学到了很多关于帐户抽象的知识。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">更多内容</h2><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/ikaBggQNSvuiotOcdufHFST3Q8eiHujuKWrgeoeLL_0">https://mirror.xyz/0xbitfly.eth/ikaBggQNSvuiotOcdufHFST3Q8eiHujuKWrgeoeLL_0</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/pJGxqKDtogHVdULLhsw8iA0FTl-XbeeXD42ZSdsxdRc">https://mirror.xyz/0xbitfly.eth/pJGxqKDtogHVdULLhsw8iA0FTl-XbeeXD42ZSdsxdRc</a></p><p><strong>原文链接：</strong></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/blog/account-abstraction-aggregate-signatures">https://www.alchemy.com/blog/account-abstraction-aggregate-signatures</a></p>]]></content:encoded>
            <author>0xbitfly@newsletter.paragraph.com (0xBitFly)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/208aace3773dca742973f9010af800f0d5069627d67c84116afe4d666fce8918.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[账户抽象之路#3-钱包创建]]></title>
            <link>https://paragraph.com/@0xbitfly/3</link>
            <guid>LhIjNNZGPDD4LOx4lDIx</guid>
            <pubDate>Sat, 01 Apr 2023 11:36:04 GMT</pubDate>
            <description><![CDATA[我们尚未解决的问题是，每个用户的智能钱包合约最初是如何在区块链上创建的。部署合约的“传统”方法是使用EOA发送一个没有接收人的交易，这个交易中包含了部署的合约代码。在这里会非常恼人，因为我们刚刚做了很多工作，就是让人可以在没有EOA的情况下与区块链交互。如果用户需要自己的EOA来开始，这一切都是为了什么？ 需要澄清我们的目的是，我们想要这样一个钱包：没有钱包的人应该能够最终在链上获得一个全新的钱包，要么用ETH支付自己的gas费（即使他们还没有钱包），要么通过找到一个代付者帮忙付gas费（我们在第2部分中涵盖了这一点），他们应该能够在不创建EOA的情况下做到这一点。 还有一个不太明显的目标，但也很重要。 当我创建新的EOA时，我可以在本地生成我的私钥，并在不发送任何交易的情况下认领我的帐户。 我可以告诉其它人我的地址，并在我自己发送交易之前，就可以开始接收ETH或其它代币。 我们希望我们的智能钱包拥有相同的属性，这意味着在我们实际部署钱包合约之前，我们应该能够告诉其他人我们的地址，并能够接收资产。前提条件：用CREATE2创建确定性的合约地址在我们实际部署合约之前，我们的地址要可...]]></description>
            <content:encoded><![CDATA[<p>我们尚未解决的问题是，每个用户的智能钱包合约最初是如何在区块链上创建的。部署合约的“传统”方法是使用EOA发送一个没有接收人的交易，这个交易中包含了部署的合约代码。在这里会非常恼人，因为我们刚刚做了很多工作，就是让人可以在没有EOA的情况下与区块链交互。如果用户需要自己的EOA来开始，这一切都是为了什么？</p><p>需要澄清我们的目的是，我们想要这样一个钱包：没有钱包的人应该能够最终在链上获得一个全新的钱包，要么用ETH支付自己的gas费（即使他们还没有钱包），要么通过找到一个代付者帮忙付gas费（我们在第2部分中涵盖了这一点），他们应该能够在不创建EOA的情况下做到这一点。</p><p>还有一个不太明显的目标，但也很重要。</p><p>当我创建新的EOA时，我可以在本地生成我的私钥，并在不发送任何交易的情况下认领我的帐户。</p><p>我可以告诉其它人我的地址，并在我自己发送交易之前，就可以开始接收ETH或其它代币。</p><p>我们希望我们的智能钱包拥有相同的属性，这意味着在我们实际部署钱包合约之前，我们应该能够告诉其他人我们的地址，并能够接收资产。</p><h2 id="h-create2" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">前提条件：用CREATE2创建确定性的合约地址</h2><p>在我们实际部署合约之前，我们的地址要可以接收资产，这其实也告诉了我们需要如何实现这一点。尽管我们还没有部署我们的钱包合约，但我们需要知道当我们最终真正部署它时，它最终会是什么地址。</p><blockquote><p>最终将部署合约但尚未部署到的地址被称为反事实地址（counterfactual address），是不是很有意思。</p></blockquote><p>实现这一目标的关键因素是<code>CREATE2</code>操作码，它正是为此而设计的，它在可以根据以下输入，确定性的计算出部署合约的地址：</p><ul><li><p>调用<code>CREATE2</code>的合约地址</p></li><li><p>一个椒盐噪声，可以是任何32字节的值</p></li><li><p>真正部署合约的init code</p></li></ul><p>init code是EVM字节码的blob，指定一个函数，该函数在执行时返回不同的EVM字节码blob，该字节码保存为新部署的智能合约。这是许多人没有意识到的一个有趣的现象：当你部署合约时，你提交的代码与合约中的代码不同。特别是，多次使用相同的init code并不能保证已部署的合约具有相同的代码，因为init code可以从存储中读取或使用TIMESTAMP等操作码。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">第一次尝试：入口点部署任意合约</h2><p>现在我们知道了CREATE2，我们的第一个计划很简单。我们让用户传递initCode，如果钱包合约还不存在，则让入口点部署该合约。首先，我们将为用户操作添加一个新字段：</p><pre data-type="codeBlock" text="struct UserOperation {
  // ...
  bytes initCode;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">bytes</span> initCode;
}
</code></pre><p>然后，我们将更新入口点的<code>handleOps</code>的验证部分，以执行以下操作：</p><p>作为验证用户操作的一部分，如果操作具有非空的<code>initCode</code>，则使<code>用CREATE2</code>部署具有该<code>initCode</code>的合约</p><p>然后像往常一样进行其余的验证：</p><ul><li><p>调用新创建的钱包的<code>validateOp</code>方法</p></li><li><p>然后，如果操作有代付者，则调用代付者的<code>validatePaymasterOp</code>方法</p></li></ul><p>它实现了上面讨论的所有目标：用户可以部署任意合约，并提前知道他们最终的地址，部署可以由代付者或用户自己支付gas费（如果他们提前将ETH存入确定但未部署的合约地址）。</p><p>但有一些缺陷，这些缺陷都围绕着这样一个事实，即我们要求用户提交字节码，然后入口点来验证所有的字节码码：</p><ul><li><p>当代付者查看用户操作时，它无法合理地分析字节码来决定是否要为它付费。</p></li><li><p>当用户提交字节码来部署合约时，他们无法立即验证他们提交的字节码是否做到了他们想要的东西。如果用户正在使用第三方工具来部署他们的合约，那么该工具是一个恶意的或被黑客入侵的工具，它可以提交<code>initCode</code>，然后将后门安装到部署的合约中，而这个行为无法轻易检测到。</p></li><li><p>回想一下第1部分，打包者希望为它包含在捆绑包中的每个操作模拟验证，这样它最终就不会包括验证失败的操作，然后它必须先自付gas费用。但由于<code>initCode</code>可以是任何代码，它很容易在模拟期间成功，但在执行期间失败。</p></li></ul><p>我们需要一种方法，让用户在不提交任意字节码的情况下部署合约，并让其他参与者能够对部署行为获得一些保证。</p><p>像往常一样，当我们想要更多的执行保证时，是时候引入一个新的智能合约了。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">更好尝试：引入工厂合约</h2><p>我们不再让入口点合约可以接受任意的字节码，并调用CREATE2方法，而是让用户自己选择一个合约去调用CREATE2方法，然后这些我们统称为工厂合约，他们可以根据特定的需求创建不同类型的智能钱包。</p><p>例如，可能有一家工厂合约生成保护其 Carbonated Courage 代币的智能钱包，而另一家工厂合约生产的钱包需要5个多签中至少3个密钥来签署交易。</p><p>工厂合约将对外暴露一个可以创建钱包合约的方法：</p><pre data-type="codeBlock" text="contract Factory {
  function deployContract(bytes data) returns (address);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Factory</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deployContract</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>)</span>;
}
</code></pre><blockquote><p>我们让工厂合约返回新创建的钱包合约的地址，以便用户可以在部署之前模拟此方法，获得合约创建的地址，这也是我们最初的目标之一。</p></blockquote><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4200f8519d91736e4b4463c248805e79b7fce70c027568026920bab9fca3c15b.jpg" alt="用户可以调用称为工厂的合约，专门创建不同种类的钱包合约" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">用户可以调用称为工厂的合约，专门创建不同种类的钱包合约</figcaption></figure><p>用户可以调用称为工厂的合约，专门创建不同种类的钱包合约。</p><p>这解决了上一节中的前两个问题！</p><ul><li><p>如果用户调用工厂合约要求保护 Carbonated Courage 代币的钱包，那么假设工厂合约经过审计，他们肯定知道他们最终会得到一个保护 Carbonated Courage 的钱包，没有后门，他们不需要检查任何字节码就可以做到。</p></li><li><p>代付者可以选择为那些经过批准的工厂合约，支付他们的部署付款（gas费）。</p></li></ul><p>上一节中的最后一个问题是部署代码在模拟期间可能成功，但在执行期间失败。</p><p>这正与我们在代付者 的 <code>validatePaymasterOp</code> 方法中遇到的问题类似，我们将以同样的方式解决它。</p><p>打包者将限制工厂合约只能访问他们自己的关联存储和他们正在部署的钱包的存储，并且不允许他们调用被禁止的方法，如 <code>TIMESTAMP</code>。</p><p>我们还将要求工厂合约通过入口点的 <code>addStake</code> 方法质押一些 ETH，然后打包者可以根据最近模拟被伪造的频率来限制或禁止此工厂合约。</p><blockquote><p>与打包者paymasters一样，如果工厂的部署方法仅访问其正在部署的钱包的关联存储，而不访问工厂合约自己的关联存储，则工厂合约无需质押。</p></blockquote><p>我们做到了！</p><p>钱包创作从未如此美好。</p><p>此时，我们创建的架构可以执行实际 ERC-4337 的所有功能！</p><p>我们将在第 4 部分中讨论的关于聚合签名的唯一剩余目标：启用优化以节省 gas，但实际上并没有添加任何新功能。</p><p>我们可以愉快地停在这里享受我们的成功创作，但如果我们想要节省那些gas费，我们可以继续前进了......</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">更多文章请阅读</h2><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/ikaBggQNSvuiotOcdufHFST3Q8eiHujuKWrgeoeLL_0">https://mirror.xyz/0xbitfly.eth/ikaBggQNSvuiotOcdufHFST3Q8eiHujuKWrgeoeLL_0</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/pJGxqKDtogHVdULLhsw8iA0FTl-XbeeXD42ZSdsxdRc">https://mirror.xyz/0xbitfly.eth/pJGxqKDtogHVdULLhsw8iA0FTl-XbeeXD42ZSdsxdRc</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/SS9HnluXYeQiKmXSjoGgj_211GTHOZ7tzQ2W2qtMy2k">https://mirror.xyz/0xbitfly.eth/SS9HnluXYeQiKmXSjoGgj_211GTHOZ7tzQ2W2qtMy2k</a></p><p>原文：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/blog/account-abstraction-wallet-creation">https://www.alchemy.com/blog/account-abstraction-wallet-creation</a></p>]]></content:encoded>
            <author>0xbitfly@newsletter.paragraph.com (0xBitFly)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/b2359e2ac2a9a817b528fe1fba7e207e91632a5873f69d5a746d1e88dba25db0.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[账户抽象之路#2-代理支付]]></title>
            <link>https://paragraph.com/@0xbitfly/2</link>
            <guid>NiGNK6LurNAdfe5hBizN</guid>
            <pubDate>Fri, 31 Mar 2023 10:40:18 GMT</pubDate>
            <description><![CDATA[在前一篇文章账户抽象之路#1-从零设计，我们完全复制了 EOA 的功能，并通过允许用户选择自己的自定义验证逻辑对智能钱包进行了改进。但是就目前而言，钱包仍然需要支付gas费，这意味着钱包所有者需要找到一种方式来获得一些ETH，然后才能在链上执行操作。 如果我们希望钱包所有者以外的人都可以代替支付gas，要怎么去实现呢？ 这么做的一些原因是：如果钱包所有者是区块链新手，那么在执行链上操作之前需要获取 ETH 是一个巨大的门槛；去中心化应用(dapp)可能愿意为其功能支付 gas 费用，这样 gas 费用就不会吓跑潜在用户；发起人可能允许钱包使用 ETH 以外的其他代币支付 gas，例如 USDC；出于隐私考虑，用户可能希望将资产从混币器提取到新地址，并将gas费记入与他们无关的帐户；引入代付者假设我是一个想要为其他人付gas的dapp应用，大概率我不想为每个人都付gas费，所以我需要在链上定义一套逻辑，用来检查用户的op，以此来决定是否为他支付gas费。 将自定义逻辑放到链上的方法是部署一个合约，我们称之为代理者（paymaster）。 它有一个方法可以查看用户的操作，并决定是否愿...]]></description>
            <content:encoded><![CDATA[<p>在前一篇文章账户<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/ikaBggQNSvuiotOcdufHFST3Q8eiHujuKWrgeoeLL_0">抽象之路#1-从零设计</a>，我们完全复制了 EOA 的功能，并通过允许用户选择自己的自定义验证逻辑对智能钱包进行了改进。但是就目前而言，钱包仍然需要支付gas费，这意味着钱包所有者需要找到一种方式来获得一些ETH，然后才能在链上执行操作。</p><p>如果我们希望钱包所有者以外的人都可以代替支付gas，要怎么去实现呢？</p><p>这么做的一些原因是：</p><ul><li><p>如果钱包所有者是区块链新手，那么在执行链上操作之前需要获取 ETH 是一个巨大的门槛；</p></li><li><p>去中心化应用(dapp)可能愿意为其功能支付 gas 费用，这样 gas 费用就不会吓跑潜在用户；</p></li><li><p>发起人可能允许钱包使用 ETH 以外的其他代币支付 gas，例如 USDC；</p></li><li><p>出于隐私考虑，用户可能希望将资产从混币器提取到新地址，并将gas费记入与他们无关的帐户；</p></li></ul><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">引入代付者</h2><p>假设我是一个想要为其他人付gas的dapp应用，大概率我不想为每个人都付gas费，所以我需要在链上定义一套逻辑，用来检查用户的op，以此来决定是否为他支付gas费。</p><p>将自定义逻辑放到链上的方法是部署一个合约，我们称之为代理者（paymaster）。</p><p>它有一个方法可以查看用户的操作，并决定是否愿意为改操作支付gas费：</p><pre data-type="codeBlock" text="contract Paymaster {
  function validatePaymasterOp(UserOperation op);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Paymaster</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validatePaymasterOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
}
</code></pre><p>然后当智能钱包提交用户操作op时，他们需要指定他们希望哪个代理者（如果有的话）来为他支付gas费。</p><p>我们将为<code>UserOperation</code>添加一个新的字段来标识它。</p><p>我们还将向用户操作Op添加一个字段，智能钱包可以使用该字段将任意数据传递给代付者，以帮助它验证，并让代付者支付其费用。</p><p>例如，这个数据可以是代付者链下签名的内容。</p><pre data-type="codeBlock" text="struct UserOperation {
  // ...
  address paymaster;
  bytes paymasterData;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">address</span> paymaster;
  <span class="hljs-keyword">bytes</span> paymasterData;
}
</code></pre><p>接下来我们将改变入口点合约的<code>handleOps</code>方法，让它使用代付者支付gas费。</p><p>他的行为如下：</p><p>对于每一个op：</p><ul><li><p>在op的发送者指定的钱包上调用<code>validateOp</code></p></li><li><p>如果op有paymaster地址，则调用该paymaster上的<code>validatePaymasterOp</code></p></li><li><p>任何验证失败的操作都将被丢弃</p></li><li><p>对于每个op，在op的发送者钱包上调用<code>executeOp</code>，跟踪使用了多少gas，然后将ETH转移给执行者以支付该 gas。如果op有<code>paymaster</code>字段，那么这个ETH来自代付者<code>paymaster</code>。否则，它像以前一样来自钱包。</p></li></ul><p>就像智能钱包一样，paymasters通过入口点的存款方式存入他们的 ETH，然后才能用于支付操作费用。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3abfaa2c3b4711d96ba0bf56f77e7b0f043aac6e982c532997e4d75b626f3ea8.jpg" alt="执行者调用代付者合约和用户的智能合约钱包来确定是否可以赞助用户的交易" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">执行者调用代付者合约和用户的智能合约钱包来确定是否可以赞助用户的交易</figcaption></figure><p>这很简单，对不对？</p><p>我们只需要打包者更新他们链下的模拟和...</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">代付者质押</h3><p>在上一篇关于钱包退款给打包者的文章中，打包者使用模拟来尝试避免执行验证失败的操作，因为这意味着钱包不会付款，因此打包者将承担 gas 费用。</p><p>同样的问题出现在这里：</p><p>打包者希望避免提交未通过代付者验证的操作，因为代付者不会付款，打包者将再次陷入困境。</p><p>起初，我们看似可以对<code>validatePaymasterOp</code>设置相同的限制，就像我们在<code>validateOp</code>上所做的那样（即它只能访问钱包及其自己的关联存储，不能使用被禁止的操作码），然后打包者可以简单地为用户操作模拟<code>validatePaymasterOp</code>，同时模拟钱包的<code>validateOp</code> 方法。</p><p>但这里有一个陷阱。</p><p>由于存储限制，即智能钱包的<code>valiateOp</code>只能访问该钱包的关联存储，我们知道，只要它们来自不同的钱包，捆绑的包中多个操作的验证就不能相互干扰，因为它们访问的共同存储空间很少。</p><p>但一个代付者的存储是被这个包中所有使用该代付者的用户操作所共享的。</p><p>这意味着一个<code>validatePaymasterOp</code>的操作可能会导致捆绑包中使用同一代付者的许多其他操作的验证失败。</p><p>恶意的代付者paymaster可以使用这个来DDoS攻击整个系统。</p><p>为了防止这种情况，我们引入了声誉系统。</p><p>我们将让打包者跟踪每个代付者最近失败验证的频率，并通过限制或禁止使用该代付者的操作来惩罚经常失败的代付者。</p><p>如果恶意的代付者可以自己创建许多实例（Sybil攻击/女巫攻击），则此声誉系统将不起作用，因此我们要求代付者质押ETH，这样它就不会从拥有多个帐户中受益。</p><p><strong>让我们在入口点添加处理质押的新方法：</strong></p><pre data-type="codeBlock" text="contract EntryPoint {
  // ...

  function addStake() payable;
  function unlockStake();
  function withdrawStake(address payable destination);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EntryPoint</span> </span>{
  <span class="hljs-comment">// ...</span>

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addStake</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">unlockStake</span>(<span class="hljs-params"></span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdrawStake</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">payable</span> destination</span>)</span>;
}
</code></pre><p>一旦押金被放入，并调用lockingStake后，经过一些延迟之后，它才能被提取。</p><p>这些新方法与之前讨论的 <code>deposit</code> 和 <code>withdrawTo</code> 不同，智能钱包和代付者使用它们来添加 ETH，这些 ETH 将用于支付 gas费， 并可以随时提现。</p><p>质押规则有一个例外：</p><p>如果代付者只访问只能钱包相关的存储，而不是代付者自己的存储，那么它就不需要质押，因为在这种情况下，捆绑中的多个操作所访问的存储不会相互重叠，原因与智能钱包的<code>validateOp</code>调用相同。</p><p>实际上，我认为了解信誉系统的详细规则并不那么重要。你可以在这里读到它们，但只要你知道打包者将有一个机制来避免从一个不好的代付者那里选择操作Op，这就足够了。</p><p>另外，每个打包者都会在本地跟踪信誉，所以如果一个打包者认为自己可以做得更好，并且不会给其他打包者带来麻烦，那么他可以自由地实现自己的信誉逻辑。</p><blockquote><p>与许多质押机制不同，这里的质押从未被削减，它们只是作为一种方式存在，要求潜在的攻击者质押一个非常大的资本，以防止进行大规模的攻击。</p></blockquote><h3 id="h-postop" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">优化代付者的postOp</h3><p>我们可以做一个小小的改进，让代付者可以做的更多。现在，代付者只在验证步骤中被调用，这在操作实际运行之前。</p><p>但是，一个代付者也可能需要根据操作的结果做一些不同的事情。</p><p>例如，一个允许用户用USDC支付gas费，那它就需要知道用户操作实际使用了多少gas费，这样它才知道该收取多少USDC。</p><p>因此，我们将为代付者添加一个新的方法，<code>postOp</code>，在用户操作完成后，入口点将调用这个方法，并传递给它使用了多少gas费。</p><p>我们还希望代付者能够 &quot;向自己传递信息&quot;，将验证过程中的结果数据放到<code>postOp</code>中计算，因此我们将允许验证返回任意的 &quot;上下文 &quot;数据，这些数据将在以后被传递给<code>postOp</code>。</p><p><strong>我们对 postOp 的第一次修改如下：</strong></p><pre data-type="codeBlock" text="contract Paymaster {
  function validatePaymasterOp(UserOperation op) returns (bytes context);
  function postOp(bytes context, uint256 actualGasCost);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Paymaster</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validatePaymasterOp</span>(<span class="hljs-params">UserOperation op</span>) <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes</span> context</span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postOp</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> context, <span class="hljs-keyword">uint256</span> actualGasCost</span>)</span>;
}
</code></pre><p>但是对于想要在最后以 USDC 收费的代付者来说，有一些棘手的事情。</p><p>理论上，代付者在批准执行之前（在<code>validatePaymasterOp</code>中）检查用户是否有足够的USDC来支付该操作，但是有可能该操作在执行过程中转移了钱包中所有的USDC，这将意味着代付者无法在最后获得付款。</p><blockquote><p>代付者能否通过在开始时收取最大金额的USDC，然后在结束时退还未使用的部分来避免这种情况？这将是可行的，但它更麻烦的：它需要做两次transfer调用，而不是一次，这增加了gas费成本，并发出了两个不同的transfer事件。我们将看看我们是否能做得更好。</p></blockquote><p>我们需要一种方法，即使操作失败了，也能够支付gas费，因为无论发生什么，它已经在<code>validatePaymasterOp</code>时同意支付gas费了。</p><p>方法是让入口点合约调用<code>postOp</code>两次。</p><p>入口点合约首先调用<code>postOp</code>，作为它刚刚运行钱包<code>executeOp</code>的同一个执行的一部分，因此，如果<code>postOp</code> 失败被回滚，它会导致<code>executeOp</code>的所有操作也回滚。</p><p>译注：如果第二次运行，则说明执行失败，同时在这里收取手续费</p><p>如果发生这种情况，那么入口点会再次调用<code>postOp</code>，但现在我们处于<code>executeOp</code>发生之前的情况，因为在这种情况下，我们刚刚检查了<code>validatePaymasterOp</code>，代付者应该能够提取它应得的。</p><p>为了给<code>postOp</code>提供更多的上下文信息，我们将给它多一个参数：一个标志，以表明我们是否在它的 &quot;第二次运行 &quot;中，因为它已经被回滚了一次：</p><pre data-type="codeBlock" text="contract Paymaster {
  function validatePaymasterOp(UserOperation op) returns (bytes context);
  function postOp(bool hasAlreadyReverted, bytes context, uint256 actualGasCost);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Paymaster</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validatePaymasterOp</span>(<span class="hljs-params">UserOperation op</span>) <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes</span> context</span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postOp</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> hasAlreadyReverted, <span class="hljs-keyword">bytes</span> context, <span class="hljs-keyword">uint256</span> actualGasCost</span>)</span>;
}
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">回顾</h2><p>为了让钱包拥有者以外的人支付gas费，我们引入了一个新的参与者，即代付者paymaster，他部署了一个具有以下接口的智能合约：</p><pre data-type="codeBlock" text="contract Paymaster {
  function validatePaymasterOp(UserOperation op) returns (bytes context);
    function postOp(bool hasAlreadyReverted, bytes context, uint256 actualGasCost);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Paymaster</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validatePaymasterOp</span>(<span class="hljs-params">UserOperation op</span>) <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes</span> context</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postOp</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> hasAlreadyReverted, <span class="hljs-keyword">bytes</span> context, <span class="hljs-keyword">uint256</span> actualGasCost</span>)</span>;
}
</code></pre><p>我们在用户操作中增加了新的字段，允许钱包指定他们想要的代付者：</p><pre data-type="codeBlock" text="struct UserOperation {
  // ...
  address paymaster;
  bytes paymasterData;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">address</span> paymaster;
  <span class="hljs-keyword">bytes</span> paymasterData;
}
</code></pre><p>代付者将ETH存入入口点合约，就像钱包支付自己的gas费一样。</p><p>入口点合约更新了<code>handleOps</code>方法，以便对每个操作op，除了通过钱包的<code>validateOp</code>进行钱包的验证外，还通过代付者的<code>validatePaymasterOp</code>对代付者（如果有）进行验证，然后执行操作，最后调用代付者的<code>postOp</code>。</p><p>为了解决模拟付款人验证中的一些异常问题，我们需要引入一个代付者质押系统，用于锁定代付者的ETH。</p><p>新的入口点合约方法如下：</p><pre data-type="codeBlock" text="contract EntryPoint {
  // ...

  function addStake() payable;
  function unlockStake();
  function withdrawStake(address payable destination);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EntryPoint</span> </span>{
  <span class="hljs-comment">// ...</span>

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addStake</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">unlockStake</span>(<span class="hljs-params"></span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdrawStake</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">payable</span> destination</span>)</span>;
}
</code></pre><p>随着 代付者paymasters 的加入，我们已经实现了大多数人在看到帐户抽象时，所能想到的所有功能！</p><p>我们也已经非常接近 ERC-4337，但我们还需要更多功能来实现最终功能。</p><p>来到这里感觉真好！</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">更多文章请阅读</h2><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/ikaBggQNSvuiotOcdufHFST3Q8eiHujuKWrgeoeLL_0">https://mirror.xyz/0xbitfly.eth/ikaBggQNSvuiotOcdufHFST3Q8eiHujuKWrgeoeLL_0</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/dashboard/edit/1bDXaYsEYzwPPRsKrjhT2iX-rLo29wYuCkFg1bSmNu4">https://mirror.xyz/dashboard/edit/1bDXaYsEYzwPPRsKrjhT2iX-rLo29wYuCkFg1bSmNu4</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/SS9HnluXYeQiKmXSjoGgj_211GTHOZ7tzQ2W2qtMy2k">https://mirror.xyz/0xbitfly.eth/SS9HnluXYeQiKmXSjoGgj_211GTHOZ7tzQ2W2qtMy2k</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/1bDXaYsEYzwPPRsKrjhT2iX-rLo29wYuCkFg1bSmNu4">https://mirror.xyz/0xbitfly.eth/1bDXaYsEYzwPPRsKrjhT2iX-rLo29wYuCkFg1bSmNu4</a></p><p><strong>原文链接</strong></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/blog/account-abstraction-paymasters">https://www.alchemy.com/blog/account-abstraction-paymasters</a></p>]]></content:encoded>
            <author>0xbitfly@newsletter.paragraph.com (0xBitFly)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/bfd1789ffc21170c7c2d5e0b3c53fb170359f62a744044ae44fd220015faa8cb.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[账户抽象之路#1-从零设计]]></title>
            <link>https://paragraph.com/@0xbitfly/1</link>
            <guid>dUhxW8KqMGE3zW1sDG8g</guid>
            <pubDate>Thu, 30 Mar 2023 11:41:08 GMT</pubDate>
            <description><![CDATA[1. 用更简单的途径理解复杂的ERC4337帐户抽象将彻底改变我们与区块链的交互方式。但ERC-4337标准中提出的帐户抽象版本晦涩难懂，很难理解为什么有这么多参与者，以及为什么他们以这种方式进行互动。 有更简单的方法吗？ 有，在这篇文章中，我将带领大家一步一步设计一个及其简单版本的账户抽象，在这一过程中，我们将看到，随着我们添加更多的需求，最终会得到一个复杂并且接近ERC-4337的抽象账户。 本文的目标读者是对智能合约有一定了解，但对账户抽象不是特别了解的人。 因为这篇文章是带领大家重新创造一个账户抽象的过程，因此会有很多API和功能的案例与ERC-4337的最终版本有所不同。 比如，在我例举出用户操作（User Operation）的字段时，请不要认为这就是真实的字段，它代表了被加工成正式版本之前的第一次尝试。 好的，让我们开始：2. 目标：创建一个可以保护你资产的钱包开始之前请，让我们创建一个可以保护我们最重要资产的方法，我们希望能够使用单个私钥签署大多数的交易（就像经典的EOA账户一样），但是我最珍贵的Carbonated Courage NFT应该由第二把钥匙签名后才...]]></description>
            <content:encoded><![CDATA[<h2 id="h-1-erc4337" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1. 用更简单的途径理解复杂的ERC4337</h2><p>帐户抽象将彻底改变我们与区块链的交互方式。但ERC-4337标准中提出的帐户抽象版本晦涩难懂，很难理解为什么有这么多参与者，以及为什么他们以这种方式进行互动。</p><p>有更简单的方法吗？</p><p>有，在这篇文章中，我将带领大家一步一步设计一个及其简单版本的账户抽象，在这一过程中，我们将看到，随着我们添加更多的需求，最终会得到一个复杂并且接近<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-4337">ERC-4337</a>的抽象账户。</p><p>本文的目标读者是对智能合约有一定了解，但对账户抽象不是特别了解的人。</p><p>因为这篇文章是带领大家重新创造一个账户抽象的过程，因此会有很多API和功能的案例与ERC-4337的最终版本有所不同。</p><p>比如，在我例举出用户操作（User Operation）的字段时，请不要认为这就是真实的字段，它代表了被加工成正式版本之前的第一次尝试。</p><p>好的，让我们开始：</p><h2 id="h-2" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2. 目标：创建一个可以保护你资产的钱包</h2><p>开始之前请，让我们创建一个可以保护我们最重要资产的方法，我们希望能够使用单个私钥签署大多数的交易（就像经典的EOA账户一样），但是我最珍贵的<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://carbonatedcourage.org/">Carbonated Courage NFT</a>应该由第二把钥匙签名后才能转移，而这个钥匙锁在一个由三头犬看管的银行金库中。</p><p><strong>第一个问题：</strong></p><p>每个以太坊账户要么是<strong>智能合约</strong>，要么是外部拥有账户（EOA），后者是通过链下私钥进行控制的，那么持有这些资产的账户应该是用智能合约还是 EOA呢？</p><p>事实上，资产的拥有方必须是智能合约，如果是EOA，那么资产总是可以通过EOA的私钥签名的交易来转移，这绕过了我们想要的安全性。</p><p>因此，与今天的大多数人不同，未来我们在链上的账户将由智能合约代替EOA，我们将称之为智能合约钱包（Smart Contract Wallet），或者只叫“钱包”（Wallet）。</p><p>我们需要定义一种方法来向这个智能合约钱包发出指令，以便它执行我们想要的操作。特别是，我们需要能够指挥这个智能合约钱包，以便像之前从EOA那样进行转账或者调用某些其它合约。</p><blockquote><p>每个希望以这种方式保护其资产的用户都需要自己的智能合约，不能用一个大的智能合约持有多人的资产，因为生态系统的其余部分已经假设一个地址代表一个实体，无法再细分其中的某个用户。例如，如果有人想在合并钱包合约中向某人发送NFT，NFT的transferAPI将只允许发件人指定合并钱包的地址，而不是其中的个人用户。</p></blockquote><h3 id="h-user-operations" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">用户操作(User Operations)</h3><p>首先我需要部署一个智能合约钱包，该合约将存放我的资产，并且有一种方法，我可以向它传递任何我想让它执行的消息。</p><p>让我们将其称为用户操作（<strong>user operation</strong> 或者 <strong>user op）。</strong></p><p>那么智能合约钱包看起来将是这个样子：</p><pre data-type="codeBlock" text="contract Wallet {
  function executeOp(UserOperation op);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Wallet</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
}
</code></pre><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">用户操作内部是什么样子？</h3><p>首先我们需要之前EOA账户发送交易（eth_sendTransaction）所需要的所有参数。</p><pre data-type="codeBlock" text="struct UserOperation {
  address to;
  bytes data;
  uint256 value; // Amount of wei sent
  uint256 gas;
  // ...
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-keyword">address</span> to;
  <span class="hljs-keyword">bytes</span> data;
  <span class="hljs-keyword">uint256</span> value; <span class="hljs-comment">// Amount of wei sent</span>
  <span class="hljs-keyword">uint256</span> gas;
  <span class="hljs-comment">// ...</span>
}
</code></pre><p>除此之外，我们需要提供一些东西来授权这个请求——智能钱包将查看的一段数据，以决定是否要执行操作。</p><p>就我们的NFT保护钱包来说，对于大多数的用户操作，我们会传递由主密钥签名的操作的签名。（这里我们定义<strong>主密钥</strong>可以签名除了涉及Carbonated Courage NFT外的所有操作，<strong>副密钥</strong>用于签名涉及Carbonated Courage NFT操作）</p><p>但是，如果用户操作正在转移我们珍贵的Carbonated Courage NFT，那么智能钱包将需要我们传递主密钥签名和副密钥签名。</p><p>我们还将传递一个nonce，以防止重放攻击，就是防止有人可以重新发送以前的用户操作来再次运行它：</p><pre data-type="codeBlock" text="struct UserOperation {
  // ...
  bytes signature;
  uint256 nonce;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">bytes</span> signature;
  <span class="hljs-keyword">uint256</span> nonce;
}
</code></pre><p>这实际上就实现了目标！</p><p>只要我的NFT由这个合约持有，在没有两个密钥签名的情况下，它就不能转移。</p><blockquote><p>虽然钱包可以选择如何选择signature和nonce字段，但我希望几乎所有的钱包都使用signature字段，以防止未经授权的各方伪造或篡改操作，同样地，我希望几乎所有的钱包都能拒绝一个它已经见过的nonce操作。</p></blockquote><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">谁来调用智能合约钱包？</h3><p>这里一个未回答的问题是如何调用executeOp（op），由于没有我私钥的签名，它不会做任何事情，我们可以让任何人尝试调用它，并且不会有任何安全风险，但我们确实需要有人真正发起调用，这个操作才能执行。</p><p>在以太坊上，所有交易都必须由EOA发起，EOA必须用自己的ETH支付gas费，才能发起调用。</p><p>我能做的是持有一个单独的EOA帐户，其唯一目的是调用我的钱包合约。虽然这个EOA不会有与钱包合约一样的双签名保护机制，但它只需要持有足够的ETH来支付我执行钱包合约的gas费即可，而更安全的钱包合约可以持有我所有有价值的资产。</p><p>因此，我们实际上只用一个非常简单的合约就实现了很大一部分帐户抽象的功能！</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3557337c39f254149c75d904b612e3e1debd7b21a4c52c2c9b1600d4e0aada00.jpg" alt="用户单独的EOA账户调用智能合约钱包" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">用户单独的EOA账户调用智能合约钱包</figcaption></figure><blockquote><p>我所说的“钱包合约”在ERC-4337中被称为“帐户”。我觉得这很令人困惑，因为我认为每个地址都是一个帐户。我总是把这位参与者称为“钱包合约”或只是“钱包”。</p></blockquote><h2 id="h-3-eoa" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3. 目标：没有单独的EOA账户</h2><p>上述解决方案的一个缺点是，我需要运行一个单独的EOA帐户来调用我的钱包。如果我不想那样做呢？<strong>目前，我仍然愿意用ETH支付自己的gas费</strong>，我只是不想有两个单独的帐户。</p><p>我们说过，钱包合约的<code>executeOp</code>方法可以被任何人调用，所以我们可以让其他有EOA的人为我们调用它。我将把这个EOA和运行它的人称为“执行者”(executor)。</p><p>由于执行者是支付gas费的人，所以没有多少人愿意免费这样做。因此，新计划是，钱包合约将持有一些ETH，作为执行人调用的一部分，钱包将向执行人转移一些ETH，以补偿执行人使用的gas费。</p><blockquote><p>“执行人Executor”不是ERC-4337术语，但它很好地描述了这位参与者的所作所为。稍后，我们将用ERC-4337使用的实际术语“打包者Bundler”替换它，但现在这样做还没有意义，因为我们目前没有进行任何打包。其他协议也可能称这个参与者为“中继者relayer”。</p></blockquote><h3 id="h-gas" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">第一次尝试：智能钱包在执行结束退还Gas费</h3><p>让我们尽量保持简单些，智能钱包的接口如下：</p><pre data-type="codeBlock" text="contract Wallet {
  function executeOp(UserOperation op);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Wallet</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
}
</code></pre><p>我们会尝试修改executeOp的行为，以便在最后查看它使用了多少 gas，并将适当数量的 ETH 发送给执行者以支付gas费用。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/64d091e6beb32ad771afca160162bd5a3e25caa0889eb496c1d2ae7c43fd7774.jpg" alt="执行者调用智能合约钱包，而非用户的EOA账户" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">执行者调用智能合约钱包，而非用户的EOA账户</figcaption></figure><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">模拟执行</h3><p>如果我的钱包值得信赖，那么这很好用！但执行者需要确保智能钱包真的会支付退款。如果执行者调用executeOp，但钱包实际上没有退还gas费，那么执行者将承担gas费。</p><p>为了避免这种情况，执行者可以尝试在本地环境模拟executeOp操作，比如用debug_traceCall，并查看它是否真的被退还了gas费。只有这样，它才会发送实际交易。</p><p>这里的一个问题是，本地模拟并不能完美地预测未来。钱包完全有可能在模拟阶段顺利支付gas费，但在实际上链时却拒绝支付gas费。一个不诚实的钱包可以故意这样做，免费执行其操作，并让执行者支付大量的gas费。</p><p><strong>由于以下几个原因，模拟可能与实际执行不同：</strong></p><ul><li><p>操作可以从区块链存储中读取数据，存储可能在模拟时与真正执行时会有不同。</p></li><li><p>该操作可以使用<code>TIMESTAMP</code>、<code>BLOCKHASH</code>、<code>BASEFEE</code>等opcode。这些opcode会从链上环境中读取信息，并且每个block都不相同，且不可预测。</p></li></ul><p>执行者可以尝试的一件事是限制允许执行的操作，例如拒绝执行所有依赖于“环境”opcode的操作，但这将是一个过于严格的限制。</p><p>请记住，我们希望智能钱包能够做EOA可以做的任何事情，所以禁止这些操作码将影响太多的正常使用。例如，它会影响智能钱包与uniswap的交互，因为uniswap大量使用了<code>TIMESTAMP</code> 。</p><p>由于钱包的<code>executeOp</code>可以包含任何代码，我们无法合理地通过限制它，来阻止它欺骗模拟器，因此这个问题在当前状态下是无法避免的，executeOp是一个彻头彻尾的黑盒。</p><h3 id="h-entry-point" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">更好的尝试：新增入口点Entry Point</h3><p>这里的问题是我们要求执行者去执行不受信任的合约代码，执行者更希望的是在授予某些保证的上下文中运行这些不受信任的操作。这就是这个智能合约的全部目的，所以我们将引入一个新的可信（即经过审计、源代码有验证）的合约，称为入口点（Entry Point），并为其提供一个执行者可调用的方法：</p><pre data-type="codeBlock" text="contract EntryPoint {
  function handleOp(UserOperation op);

  // ...
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EntryPoint</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;

  <span class="hljs-comment">// ...</span>
}
</code></pre><p><code>handleOp</code> 会做以下事情：</p><ul><li><p>检查智能钱包是否有足够的资金来支付它可能使用的最大 gas 费（基于用户 op 中的 gas 字段）；如果不是，则拒绝。</p></li><li><p>调用钱包的 <code>executeOp</code> 方法（使用适当的gas），并跟踪它实际使用了多少gas。</p></li><li><p>将智能钱包的一些 ETH 发送给执行者以支付 gas。</p></li></ul><p>为了使第三个要点起作用，我们实际上需要入口点合约持有一定的 ETH来支付gas ，而不是钱包本身，因为正如我们在上一节中看到的那样，我们无法确定是否能够取出智能合约钱包里的ETH，来支付执行者的gas费。 因此，入口点合约还需要智能钱包（或代表钱包的人）将 ETH 放入入口点以支付其 gas 的接口，我们还需要另一个接口，以便钱包可以在以它想要的时候取回其 ETH ：</p><pre data-type="codeBlock" text="contract EntryPoint {
  // ...

  function deposit(address wallet) payable;
  function withdrawTo(address payable destination);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EntryPoint</span> </span>{
  <span class="hljs-comment">// ...</span>

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> wallet</span>) <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdrawTo</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">payable</span> destination</span>)</span>;
}
</code></pre><p>通过这个实现方案，执行者无论如何都会得到gas费的退款。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f129b09ecb57269a391cf53ba8c17fc85c700bd91f649aa3e9120752f4332986.jpg" alt="引入经过审计、源代码验证的入口点合约，以确保执行者得到补偿。" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">引入经过审计、源代码验证的入口点合约，以确保执行者得到补偿。</figcaption></figure><p>这对执行者来说很棒！但这对钱包来说实际上是一个很大的问题...…</p><blockquote><p>智能钱包不应该能够使用自己的 ETH， 而不是存在入口点的 ETH 来支付 gas 费用吗？是的，应该是这样！我们会谈到这一点，但在我们从下一节进行更改之前我们不能这样做，即使那样我们仍然需要存款/取款接口。另外，我们稍后需要存款/取款系统来支撑代理者的一些操作。</p></blockquote><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">拆分验证和执行环节</h3><p>我们之前定义的智能钱包接口是这样：</p><pre data-type="codeBlock" text="contract Wallet {
  function executeOp(UserOperation op);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Wallet</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
}
</code></pre><p>这个接口实际上会做两件事：首先验证用户操作是否已授权，然后执行用户指定的op操作。当钱包的所有者用自己的帐户支付gas费时，这种区别并不重要，但现在我们要求执行者去付gas费，这种区分就很重要了。</p><p>我们目前的方案无论在何种情况下，钱包都会将gas费退还给执行者，但我们实际上不希望智能钱包在验证失败时付款。</p><p>如果验证失败，则意味着提交这个op的人，没有权限要求该智能钱包做任何事情。</p><p>在这种情况下，智能钱包的<code>executeOp</code>将正确地阻止操作，但在目前的实现方式下，智能钱包仍然需要支付 gas。</p><p>这是一个问题，因为与智能钱包无关的人可以从该钱包请求一系列操作，并用完钱包的所有gas费用。</p><p>相比之下，如果验证成功，但之后操作失败，则钱包应该被收取 gas 费用。 这表示钱包所有者授权了一项执行失败的操作，就像从 EOA 发送被拒绝的交易一样，并且因为他们授权了它，所以他们应该对产生的gas费负责。</p><p>目前这个方法的钱包接口没有提供区分验证失败和执行失败的接口，所以我们需要把它分成两部分。</p><p>我们新的智能钱包接口是：</p><pre data-type="codeBlock" text="contract Wallet {
  function validateOp(UserOperation op);
  function executeOp(UserOperation op);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Wallet</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
}
</code></pre><p>新的实现下的入口点合约操作将是：</p><ul><li><p>调用<code>validateOp</code> ，如果失败，则停止；</p></li><li><p>从智能钱包在入口点合约的存款中预留出一部分ETH ，以支付op可能使用的最大 gas 费（基于 op<code>gas</code>的字段），如果钱包不够，则拒绝。</p></li><li><p>调用 <code>executeOp</code> 并跟踪它使用了多少 gas，无论这次调用成功还是失败，从我们预留的ETH资金中退还执行者的 gas，并将其余资金退还给钱包的存款处。</p></li></ul><p>现在智能钱包看起来很棒！除授权的操作外，它不会收取gas费用。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f61c12fbcff7d709939ff0d4f9105d5faeb4560f9ac02c1c1bda674befd4f86f.jpg" alt="拆分验证和执行操作，以区分验证失败和执行失败。" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">拆分验证和执行操作，以区分验证失败和执行失败。</figcaption></figure><p>但对于执行者来说，事情看起来又变得有风险。。。</p><blockquote><p>我们应该确保未经授权的用户不能直接调用钱包上的 executeOp，防止它在未经验证的情况下采取行动。钱包可以通过强制只能由入口点调用 executeOp 来防止这种情况。</p><p>为什么不诚实的智能钱包不直接在 validateOp 中执行所有操作，这样如果执行失败，它就不会被收取 gas 费用。稍后我们将看到 validateOp 将具有很大的限制，使其不适合“真实”执行阶段调用。</p></blockquote><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">重新模拟</h3><p>现在，当未授权用户提交对钱包的操作时，<code>validateOp</code> 操作将失败，钱包无需支付gas费，但是executor还是会为<code>validateOp</code>的链上执行支付gas费用，不会得到相应的gas补偿。</p><p>不诚实的钱包无法再让他们的操作免费运行，但恶意的人仍然可以随时通过让执行者因失败的操作，而在 gas 上损失钱。</p><p>在前面的模拟部分，执行者先在本地尝试模拟操作，看看是否能通过，然后才提交交易，调用链上的<code>handleOp</code>。</p><p>我们之前遇到问题是，执行者无法合理地限制用户的执行，以防止它在模拟时成功，但在真实交易中却失败。</p><p>但这次却有些不同。</p><p>现在执行者不需要模拟整个执行过程，执行的步骤被拆分成<code>validateOp</code>和<code>executeOp</code>，执行者只需要模拟第一部分<code>validateOp</code>，就可以知道他是否能拿回执行的gas费，并且不像 <code>executeOp</code> 需要能够执行任何操作，以便钱包可以自由地与区块链交互，我们可以对 <code>validateOp</code> 施加更严格的限制。</p><p><strong>具体来说，除非validateOp满足一下限制，否则执行者有权利拒绝用户提交的操作，不将其提交到链上：</strong></p><ol><li><p>它不能使用黑名单中的操作码，比如<code>TIMESTAMP</code>, <code>BLOCKHASH</code> 等。</p></li><li><p>它只能获取和这个智能钱包相关的存储：</p><ul><li><p>智能钱包自己的存储</p></li><li><p>另一个合约在映射中对应于钱包的插槽中的存储（地址=&gt;值）</p></li><li><p>另一个合约在与钱包地址相同的存储槽中的存储（这是一种不寻常的存储方案，在 Solidity 中通常不会出现）</p></li></ul></li></ol><p>这些规则都是为了最小化<code>validateOp</code> 在模拟时成功，但在实际执行时却失败的情况。</p><p>被禁止的操作码是理所当然的，但这些存储限制看起来可能有点奇怪。</p><p>这个想法是任何存储访问都代表了错误模拟的危险，因为存储槽可能在模拟和真正执行时发生改变，但是如果我们将存储限制在与这个钱包相关联的位置，那么恶意攻击者将需要更新攻击的钱包，更新此存储的成本足以阻止攻击者。</p><p>有了这个模拟，钱包和执行者都是安全的。</p><blockquote><p>这种存储限制还有另一个好处，那就是我们知道调用validateOp在不同钱包上进行操作不太可能相互干扰，因为它们都可以访问的存储空间是有限的，当我们谈论打包时，这更加重要。</p></blockquote><h3 id="h-gas" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">改进：从智能钱包付gas费</h3><p>之前的策略是，智能钱包通过向入口点合约存入ETH作为gas费用，然后才能发送用户操作，但是，原来的EOA账户是通过自己的钱包支付gas费，我们的智能钱包不应该也是如此么？</p><p>我们现在可以通过拆分验证和执行环节这样做了，因为入口点可以要求钱包将 ETH 作为验证步骤的一部分发送到入口点合约，否则操作会被拒绝。</p><p>我们将更新钱包的<code>validateOp</code>接口，以便入口点合约可以向它请求资金，如果 <code>validateOp</code> 没有向入口点合约支付它请求的金额时，则入口点拒绝该操作：</p><pre data-type="codeBlock" text="contract Wallet {
  function validateOp(UserOperation op, uint256 requiredPayment);
  function executeOp(UserOperation op);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Wallet</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateOp</span>(<span class="hljs-params">UserOperation op, <span class="hljs-keyword">uint256</span> requiredPayment</span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
}
</code></pre><p>由于在验证时我们不知道执行期间将使用的确切gas费用，因此入口点会根据操作Op的gas字段费用，获取执行的最大gas费用，然后在执行结束的时候，入口点合约要把没用完的gas钱还给智能钱包。</p><p>但这里我们遇到了问题</p><p>在编写智能合约时，很难将 ETH 发送到任意合约，因为这样做会调用该合约上的任意代码，这可能会失败，使用不可预估的gas费，甚至尝试对我们进行重入攻击，所以我们不会直接把多余的 gas 钱打回钱包。</p><p>相反，入口点合约会保留这部分gas费用，并允许智能钱包之后通过点用一定的方法提取这部分资金。这就是<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/#favor-pull-over-push-for-external-calls">拉动支付模式</a>（<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/#favor-pull-over-push-for-external-calls">pull-payment pattern</a>）。</p><p>所以我们实际上要做的是，通过<code>deposit</code> 方法将足额的 gas 费用存储到同一个地方，钱包可以稍后使用<code>withdrawTo</code>将其取出。</p><p>事实证明我们确实需要存款/取款接口系统（或者至少它的取款部分）。</p><p>这意味着钱包的 gas 支付实际上可以来自两个不同的地方：入口点合约持有的 ETH，以及智能钱包本身持有的 ETH。</p><p>入口点合约将首先尝试使用存入的 ETH 支付 gas，然后如果存入的 ETH 不够，它将在调用智能钱包的 <code>validateOp</code> 时索取剩余部分。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">执行者激励</h3><p>目前，作为执行者是一项吃力不讨好的工作。他们需要运行大量仿真模拟，却没有任何利润，有时当他们碰到用户操作Op被伪造时，他们被迫自掏腰包支付gas费。</p><p>为了补偿执行者，我们将允许智能钱包所有者提交带有用户操作时带上小费，该小费将发送给执行者。</p><p><strong>我们将在用户操作中添加一个字段来实现这一点：</strong></p><pre data-type="codeBlock" text="struct UserOperation {
  // ...
  uint256 maxPriorityFeePerGas;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">uint256</span> maxPriorityFeePerGas;
}
</code></pre><p>与常规交易中字段一样，<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas">maxPriorityFeePerGas</a>表示发送方愿意支付的小费额度，以便执行者优先处理他们的操作。</p><p>执行者在发送其交易，调用入口点的<code>handleOp时</code>，可以选择较高的<code>maxPriorityFeePerGas</code>，并将差额收入囊中。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">入口点合约作为唯一入口</h3><p>我们讨论了入口点如何成为受信任的合约以及它的作用，您可能会注意到，关于入口点的任何内容都不是限定于智能钱包或执行者的，因此，入口点可以是整个生态系统中的唯一入口。所有钱包和所有执行者都将与同一个入口点合约进行交互。</p><p>这意味着我们需要调整用户操作，以便其他人可以知道来自哪个用户，这样当操作被传递到入口点的 handleOp 时，入口点就可以知道要请求验证和执行的是哪个智能钱包。</p><p><strong>让我们更新一下：</strong></p><pre data-type="codeBlock" text="struct UserOperation {
  // ...
  address sender;
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">UserOperation</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">address</span> sender;
}
</code></pre><h2 id="h-4-eoa" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">4. 回顾无EOA的流程</h2><p>我们的目标是创建一个链上钱包，无需其所有者管理单独的 EOA ，也可以支付自己的 gas，现在我们已经实现了！</p><p><strong>我们将拥有一个带有如下接口的智能钱包：</strong></p><pre data-type="codeBlock" text="contract Wallet {
  function validateOp(UserOperation op, uint256 requiredPayment);
  function executeOp(UserOperation op);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Wallet</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateOp</span>(<span class="hljs-params">UserOperation op, <span class="hljs-keyword">uint256</span> requiredPayment</span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
}
</code></pre><p><strong>我们还有一个带有如下接口的全区块链唯一入口：</strong></p><pre data-type="codeBlock" text="contract EntryPoint {
  function handleOp(UserOperation op);
  function deposit(address wallet) payable;
  function withdrawTo(address destination);
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EntryPoint</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> wallet</span>) <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdrawTo</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> destination</span>)</span>;
}
</code></pre><p>当智能钱包所有者想要执行某项操作时，他们会制定一个特定的用户操作，并在链下请求执行者为他们处理。</p><p>执行者在这个用户操作上模拟钱包的<code>validateOp</code>方法来决定是否接受它。</p><p>如果接受，则执行程序将事务发送到入口点以调用<code>handleOp</code>。</p><p>然后入口点处理链上操作的验证和执行，然后从该智能钱包存入的资金中，将 ETH 退还给执行者。</p><p>哇！</p><p>内容很多，但我们做到了！</p><h2 id="h-5" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">5. 插曲: 打包</h2><p>在介绍下一个重要功能之前，让我们花点时间进行一个非常简单的优化。</p><p>到目前为止，我们已经实现了执行者发送一个事务来执行一个用户的操作，但是现在我们有了一个不只绑定到一个智能钱包的入口点合约，我们可以通过收集来自不同人的一堆用户操作，以此来节省一些 gas费，然后在一个事务中执行它们！</p><p>这种通过打包用户操作的方式，将通过不再重复支付固定的 21,000 gas 费用来发送交易，达到降低执行冷存储访问的费用来节省 gas（在第一次交易中多次访问相同的存储更便宜）。</p><p>这又需要一些更新的改动。</p><p><strong>我们将替换：</strong></p><pre data-type="codeBlock" text="contract EntryPoint {
  function handleOp(UserOperation op);
  
  // ...
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EntryPoint</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleOp</span>(<span class="hljs-params">UserOperation op</span>)</span>;
  
  <span class="hljs-comment">// ...</span>
}
</code></pre><p>成下面的：</p><pre data-type="codeBlock" text="contract EntryPoint {
  function handleOps(UserOperation[] ops);

  // ...
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">EntryPoint</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleOps</span>(<span class="hljs-params">UserOperation[] ops</span>)</span>;

  <span class="hljs-comment">// ...</span>
}
</code></pre><p>基本上就是这样：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/531174d09113215abea1810c81acbe2d92ffca2c81500a99cfdbb5b2aca3e482.jpg" alt="在单个事务中打包和执行一堆用户操作。" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">在单个事务中打包和执行一堆用户操作。</figcaption></figure><p>新的<code>handleOps</code>方法或多或少实现了你所期望的：</p><ul><li><p>对于每个操作，在操作的发送者钱包上调用<code>validateOp</code> 方法，丢弃任何未通过验证的操作。</p></li><li><p>对于每个 op，在 op 的发送者钱包上调用<code>executeOp</code> 方法，跟踪操作使用了多少 gas，然后将这部分 ETH 转移给执行者以支付该 gas。</p></li></ul><p>这里需要注意的一件事是，我们首先启动所有验证op，然后才启动所有执行op，而不是在继续执行下一个操作之前，去验证和执行每个操作。</p><p>这对于仿真模拟很重要。</p><p>如果在<code>handleOps</code>期间我们在验证下一个操作之前执行了一个操作，那么第一个op的执行会扰乱第二个op验证所依赖的存储，并导致它失败，即使第二个操作在我们模拟时通过了。</p><p>沿着类似的思路，我们希望避免一个op的验证与后面一个op的验证混淆的情况。</p><p>只要打包中不包含同一钱包的多个操作，我们实际上是免费获得的，因为上面讨论的存储限制：如果两个操作的验证不涉及相同的存储，它们就不会干扰彼此。为了利用这一点，执行者将确保一个包最多含有给定钱包的一个操作。</p><p>对执行者来说，一件好事是他们有了新的收入来源！</p><p>执行者有机会以有利于他们的方式，在打包中安排用户操作（并可能插入他们自己的操作）来获得一些<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/overviews/what-is-mev">最大可提取价值 (MEV) 。</a></p><p>现在我们有了打包，我们可以不再称这些参与者为“执行者”，而是开始用他们的真名称呼他们：<strong>打包者</strong>。</p><p>为了与 ERC-4337 术语一致，我将在接下来的文章中称它们为打包者，但实际上在我的脑海中，我发现“执行者”是更好的表述方式，因为它强调了执行模式是通过从 EOA 发送交易来真正开始执行链上的操作。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">打包者作为矿工</h3><p>我们有一个设置，钱包所有者将用户操作提交给打包者，希望将这些操作包含在捆绑的包中。这与普通交易的设置非常相似，账户所有者将交易提交给区块构建者（矿工），希望将这些交易包含在一个区块中，因此我们可以从一些相同的网络架构中受益。</p><p>正如节点将普通交易存储在内存池（<code>memory pool</code>）中并将<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/overviews/transaction-propagation">它们广播到其他节点</a>一样，打包者也可以将经过验证的用户操作存储在内存池（<code>mempool</code>）中【注：这两个内存池并不相同】，并将它们广播到其他打包者。打包者可以与其他打包者共享验证后用户操作，从而节省彼此验证每个操作的工作量。</p><p>打包者也可以通过成为矿工而受益，因为如果他们可以选择他们的打包所在的区块，他们可以减少甚至消除成功模拟后在执行过程中操作失败的可能性。此外，区块的构建者（矿工）和打包者可以通过类似 MEV 方式受益。</p><p>现在，我们可以将打包者和矿工合并为同一角色了。</p><p>哇！那又将是更多的内容了。</p><p>到目前为止，我们已经弄清楚了如何创建一个智能合约钱包来保护我们最有价值的资产，以及如何依靠执行者或打包者者，来代我们调用这个智能合约钱包。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">继续阅读：</h2><p>接下来，你将了解到代理支付、钱包创建和聚合签名！</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/1bDXaYsEYzwPPRsKrjhT2iX-rLo29wYuCkFg1bSmNu4">https://mirror.xyz/0xbitfly.eth/1bDXaYsEYzwPPRsKrjhT2iX-rLo29wYuCkFg1bSmNu4</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xbitfly.eth/SS9HnluXYeQiKmXSjoGgj_211GTHOZ7tzQ2W2qtMy2k">https://mirror.xyz/0xbitfly.eth/SS9HnluXYeQiKmXSjoGgj_211GTHOZ7tzQ2W2qtMy2k</a></p><p>原文地址：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/blog/account-abstraction">https://www.alchemy.com/blog/account-abstraction</a></p>]]></content:encoded>
            <author>0xbitfly@newsletter.paragraph.com (0xBitFly)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/185d01d9f4852b5047c2891464bfedf3dcf9b318abea6b81a9ccbf823d7d39d7.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Web3学习笔记#00-Web3应用架构]]></title>
            <link>https://paragraph.com/@0xbitfly/web3-00-web3</link>
            <guid>9DzYFe8T7GapDvqztB6U</guid>
            <pubDate>Sat, 19 Nov 2022 06:31:34 GMT</pubDate>
            <description><![CDATA[最近开始开始学习web3开发，看网上很多人建议从Solidity开始学起，我的思路是不要一头就扎进合约的学习中，只见树木不见森林，而是先从宏观整体上了解一下整体知识结构，在后面的学习中再去深入各个方面，也就是自顶向下的学习过。这里非常推荐这篇文章，本文也大部分基于此文，再加上自己的理解：the architecture of a web3.0 application，一边学习一边记录。 Web3现在一切的繁荣都起步自于中本聪2008年的一篇论文：**A Peer-to-Peer Electronic Cash System。**整片文章的核心是如何构建一套去中心化的点对点的电子现金交易系统，而Web3整个的概念体系都是构建在区中心化 这一前提下，也就是所有自己东西的主权都掌握自己手里，无须第三方平台或者媒介。 在这样一个前提下，先从用户的层面看看，相比web2有哪些改变：登录方式：这是最直接的改变，用户不需要在各个应用之间去注册各种各样的账号，用户用一个账号就可以访问所有应用；资产权限：用户的资产都存储在一个透明的链上，在未经用户授权的情况下，别人无权修改，这里的资产可以是金融货...]]></description>
            <content:encoded><![CDATA[<p>最近开始开始学习web3开发，看网上很多人建议从Solidity开始学起，我的思路是不要一头就扎进合约的学习中，只见树木不见森林，而是先从宏观整体上了解一下整体知识结构，在后面的学习中再去深入各个方面，也就是自顶向下的学习过。这里非常推荐这篇文章，本文也大部分基于此文，再加上自己的理解：<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application">the architecture of a web3.0 application</a>，一边学习一边记录。</p><p>Web3现在一切的繁荣都起步自于中本聪2008年的一篇论文：**<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bitcoin.org/bitcoin.pdf">A Peer-to-Peer Electronic Cash System</a>。**整片文章的核心是如何构建一套去中心化的点对点的电子现金交易系统，而Web3整个的概念体系都是构建在<code>区中心化</code> 这一前提下，也就是所有自己东西的主权都掌握自己手里，无须第三方平台或者媒介。</p><p>在这样一个前提下，先从用户的层面看看，相比web2有哪些改变：</p><ul><li><p><strong>登录方式</strong>：这是最直接的改变，用户不需要在各个应用之间去注册各种各样的账号，用户用一个账号就可以访问所有应用；</p></li><li><p><strong>资产权限</strong>：用户的资产都存储在一个透明的链上，在未经用户授权的情况下，别人无权修改，这里的资产可以是金融货币、社交信息、头像等等；</p></li></ul><p>从开发者的角度来看，Web2的应用与Web3应用的区别，通常一个Web系统主要由以下几个部分组成交互层、计算层和数据层，不同领域的系统在这三方面可能各有侧重，比如各大在线文档软件侧重在交互层和存储层，AI平台侧重在计算层。购物平台在交互层、计算层和数据层都重要。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/83530e7f3ffc060fb5aed5ad2ca38b8db991ce38d923d2cd08dcea18f5bfe4b7.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>目前web3的应用主要以Web3 Hybrid+为主，一些逻辑不复杂的应用是纯去中心化的。</p><h2 id="h-web3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Web3开发架构</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/968074d3eda7e1c2d0a07422ac9d0b4ea839e2bb72cfde133fc56f62038a5062.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>我们以在Uniswap上用USDT购买ETH为例，并结合上图说明整个web3应用程序的架构。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3beccc28de50a41c3f9f391fd5e694698f1096e23fa071dd630be1e9cc7783f9.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>最上面的个人用户通过浏览器访问Uniswap.org的页面，并通过钱包链接到该网站，然后在swap界面里设置好要交互的Token，点击交换后，钱包会弹出让你签名授权。这里涉及到两个重要的模块：前端界面(Front-end)和钱包插件，当然我们也可以用代码直接通过私钥与合约进行交互，这里前端界面和本地代码库一般使用web3.js/ethers.js库，而钱包通常是metamask。对于开发人员来说这里最重要是可以与节点提供商通信的前端库</p></li><li><p>用户签名成功后，就需要将该授权后的签名交易信息通过一个节点(Providor)将信息广播到全网，矿工拿到这些签名的信息后进行验证。对开发者而言，这个节点通常会使用infura/alchemy，这也需要在前端界面中提供相应的接入点，如果用程序也是一样。</p></li><li><p>在验证成功后，区块链上的合约程序就会执行该token的交换服务。对开发者而言，不同的任务需要编写不同功能的合约程序，主流的Solidity。但在服务层/业务层上，简单的业务可以都用智能合约来写，并部署在区块链上，但对于一些复杂的程序，通常还需要web2的中心化服务来提供支撑。</p></li><li><p>最后执行成功后，你的交易记录就会记录在链上，这就涉及到数据的存储，因为链上存储数据要花费更多的费用，简单的数据可以存储在链上，对复杂的数据，比如NFT的metadata和图片就需要通过IPFS这一类去中心化的存储链来存储。</p></li><li><p>那么在我们swap过程中，如果将程序现在的数据状态展现给用户呢？比如交易池中还有多少余额，最近有哪些人交易过呢？ 这就可以通过The Graph/Dune这一类链上数据索引引擎获取这些数据。当然你也可以通过区块链的客户端/节点实时监听这些数据。</p></li></ol><h2 id="h-web3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Web3细分领域</h2><p>在了解了web3整体的开发知识结构后，接下来要做的是深入了解各个细分领域的专业知识，同样也需要学习这个领域现有的整体知识结构，但在部分我们将与业务结合，学习技术如何服务具体的业务场景，各个领域的文章链接说明已经附在文后。</p><h3 id="h-nft" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">NFT</h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3db149bb6e0376c9dd8ae69a6e9b878a38091f8a874a5da7334c40d2666cf656.png" alt="NFT Landscape" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">NFT Landscape</figcaption></figure><h3 id="h-defi" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">DeFi</h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5d18817a27b08cc8bdd1c8ffadbf5d0cbb6dbd85df79ea4207a414464ada11c7.jpg" alt="DeFi Landscape" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">DeFi Landscape</figcaption></figure><h3 id="h-social" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Social</h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><figcaption HTMLAttributes="[object Object]" class="">Social Landscape</figcaption></figure><h3 id="h-games" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Games</h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e55f0831d667db8c683801d2dfee0c53f5cefe037ab1de5c603724aae2ddfa61.png" alt="Game Landscape" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Game Landscape</figcaption></figure><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">数据相关</h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/dd7dbd3b74e2f92dde9cd7348bc71701f2deae70a4ce994595d148442a6030b3.jpg" alt="Data-Related Landscape" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Data-Related Landscape</figcaption></figure><h2 id="h-web3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Web3开发常用工具</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5f2871306447627829d31849240d6291f4d56f84394edb1e04d585cbe811de15.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-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">实践</h2><p>纸上得来终觉浅，接下来就是实践学习，巩固理解，这里主要跟随是alchemy的Road to Web3，一边学习一边做笔记，初步计划是以下几个部分：</p><ul><li><p>Buy Me a Coffee：从0到1的链上转账应用</p></li><li><p>Create a PFP NFT Collection：NFT中的PFP类合集</p></li><li><p>Build a DEX Platform：DeFi中的区中心化交易所</p></li><li><p>Make a Social Platform：建一个去中心化化的Twitter</p></li><li><p>Launch a Play2Earn Game：GameFi中的Play2Earn小游戏</p></li><li><p>Build a CEX TVL Monitor Website ：链上数据中的中心化交易所地址监控</p></li></ul><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">参考</h2><p><strong>整体结构</strong></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application">https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etekisalp.com/part-3-how/">https://etekisalp.com/part-3-how/</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://itnext.io/top-3-web-3-0-architecture-layers-explained-frontend-backend-and-data-e10200f7fc76">https://itnext.io/top-3-web-3-0-architecture-layers-explained-frontend-backend-and-data-e10200f7fc76</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zhuanlan.zhihu.com/p/523641946">https://zhuanlan.zhihu.com/p/523641946</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ahmet/awesome-web3">https://github.com/ahmet/awesome-web3</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://julianivaldy.medium.com/100-tools-to-create-and-grow-your-web3-startup-nft-defi-dapp-dao-5dc466af375e">https://julianivaldy.medium.com/100-tools-to-create-and-grow-your-web3-startup-nft-defi-dapp-dao-5dc466af375e</a></p><p><strong>实践教程</strong></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.alchemy.com/docs/welcome-to-the-road-to-web3">https://docs.alchemy.com/docs/welcome-to-the-road-to-web3</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://buildspace.so/">https://buildspace.so/</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://learnweb3.io/courses">https://learnweb3.io/courses</a></p><p><strong>其它</strong></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://multicoin.capital/2020/11/24/the-defi-stack/">https://multicoin.capital/2020/11/24/the-defi-stack/</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://messari.io/report/blockchains-changing-the-game">https://messari.io/report/blockchains-changing-the-game</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://messari.io/report/the-nft-stack-exploring-the-nft-ecosystem">https://messari.io/report/the-nft-stack-exploring-the-nft-ecosystem</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/zk7hao/status/1576492616715116547?s=20&amp;t=RjcUaQfwXuAtXahn5QhgsA">https://twitter.com/zk7hao/status/1576492616715116547?s=20&amp;t=RjcUaQfwXuAtXahn5QhgsA</a></p>]]></content:encoded>
            <author>0xbitfly@newsletter.paragraph.com (0xBitFly)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/7178216758f7d7720bdb32cd115f2073cbfec6a5ae17cd74561c1ea9ecfe9de0.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>