<?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>0xlukema</title>
        <link>https://paragraph.com/@lukema95</link>
        <description>Coder</description>
        <lastBuildDate>Wed, 06 May 2026 14:38:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>0xlukema</title>
            <url>https://storage.googleapis.com/papyrus_images/0437cf3414e976c5af75311541a51dc7e4f7c8e1f0e65ae5c32ba801ffb8d711.jpg</url>
            <link>https://paragraph.com/@lukema95</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[ZetaChain:跨链赛道的黑马？]]></title>
            <link>https://paragraph.com/@lukema95/zetachain</link>
            <guid>mr48FDS6xidgMENmxQ7E</guid>
            <pubDate>Tue, 31 Oct 2023 02:06:34 GMT</pubDate>
            <description><![CDATA[介绍ZetaChain 是一个基于Cosmos-SDK开发Layer1公链项目，其主要目标是提供跨链互操作性，其最大特点是不仅支持智能合约链的跨链，更支持BTC等非智能合约链的资产跨链。 如今公链赛道越来越繁荣，不仅是通用的公链，像链游专用链以及近年来火热的Layer2等公链更是如雨后春笋般涌现，随着公链的增加，用户资产会变得更加割裂，这就需要一种方法来将资产方便的在各个公链间快速流转以此来提高资金的利用率，资产跨链便成了刚需，于是便出现个各种跨链桥。但是近年来跨链桥却成了黑客的提款机，屡屡有出现跨链桥被黑的事件，其中不乏有涉及数亿美金损失的案件。同时BTC作为区块链行业的指标，由于其不支持智能合约，而大多数跨链桥核心是基于合约设计的，所以一直无法支持BTC跨链。ZetaChain 的出现，给上述问题提供了一个解决的方案。 目前ZetaChain已经支持Bitcoin，Ethereum，BSC和Polygon链，除了比特币，后三个都是EVM兼容链，可以部署Solidity合约。整体架构ZetaChain是基于Cosmos-SDK进行进行开发的，Cosmos-SDK是一个模块化的区...]]></description>
            <content:encoded><![CDATA[<h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">介绍</h1><p><code>ZetaChain</code> 是一个基于<code>Cosmos-SDK</code>开发Layer1公链项目，其主要目标是提供跨链互操作性，其最大特点是不仅支持智能合约链的跨链，更支持BTC等非智能合约链的资产跨链。</p><p>如今公链赛道越来越繁荣，不仅是通用的公链，像链游专用链以及近年来火热的Layer2等公链更是如雨后春笋般涌现，随着公链的增加，用户资产会变得更加割裂，这就需要一种方法来将资产方便的在各个公链间快速流转以此来提高资金的利用率，资产跨链便成了刚需，于是便出现个各种跨链桥。但是近年来跨链桥却成了黑客的提款机，屡屡有出现跨链桥被黑的事件，其中不乏有涉及数亿美金损失的案件。同时BTC作为区块链行业的指标，由于其不支持智能合约，而大多数跨链桥核心是基于合约设计的，所以一直无法支持BTC跨链。ZetaChain 的出现，给上述问题提供了一个解决的方案。</p><p>目前ZetaChain已经支持Bitcoin，Ethereum，BSC和Polygon链，除了比特币，后三个都是EVM兼容链，可以部署Solidity合约。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">整体架构</h1><p><code>ZetaChain</code>是基于<code>Cosmos-SDK</code>进行进行开发的，<code>Cosmos-SDK</code>是一个模块化的区块链开发框架，其典型架构如下图所示。最上面部分为用户自定义的模块，例如EVM模块。中间层为共识模块，负责对交易达成共识以及区块处理；最下面一层为网络层，用于各个节点之间的网络通信。当交易通过网络层到达共识层，共识层根据不同的交易类型将交易路由到对应的模块进行处理，模块处理完成后将结果返回给共识层，共识层和应用层之间通过ABCI接口进行通信。举个不太恰当的例子方便理解，底下的共识层和网络层可以类比成操作系统，应用层类比成操作系统上安装的应用。这样，特定应用链只要在应用层上进行拓展，增加对应的应用(或者说模块)来完成特定的功能。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/03b708a4d062a14767ef23e18574dc12f95649d0fcec9273af6bfe9c217400bf.png" alt=" 图片来源：Cosmos-SDK文档" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">图片来源：Cosmos-SDK文档</figcaption></figure><p><code>ZetaChain</code> 在<code>Cosmos-SDK</code>的基础上在其应用层增加了四个模块用来拓展其功能，分别是<code>crosschain</code>，<code>emissions</code>，<code>fungible</code>和<code>observer</code>模块。其中<code>crosschain</code>是核心模块，主要实现跨链的核心逻辑。<code>emissions</code>模块负责协调观察者、验证者和TSS签名者的奖励分配。<code>fungible</code>负责提供同质化代币的功能以及提供EVM的操作入口。<code>observer</code>模块追踪和处理入站、出站交易等。这个部分下面会具体介绍。</p><p><code>ZetaChain</code>的验证者节点 Validator 由两个主要组件组成：<code>ZetaCore</code> 和 <code>ZetaClient</code>。这两个组件可以分别运行，<code>ZetaCore</code> 是上述描述的<code>Cosmos-SDK</code> 的拓展，则生成区块并维护复制状态机。<code>ZetaClient</code> 是客户端节点，其主要有由<code>Observer</code>和<code>TSS Signer</code>两个服务组成，前者主要用于观察外部链和ZetaChain的交易信息，后者主要用于对出入站交易和外部链交易进行签名。</p><p><code>ZetaCore</code> 和 <code>ZetaClient</code> 被捆绑在一起，由节点运营方运行。只要质押足够的资金，任何人都可以成为节点运营方来参与验证。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9d05de0a3f8d3bae9dc5e690104a68010219573b90a0d9c23a08d8fc3bbc87d5.png" alt="图片来源：ZetaChain" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">图片来源：ZetaChain</figcaption></figure><p>Validator 由三种不同的角色组成，分别是Basic Validators, Observers, and TSS signer。</p><ul><li><p>Basic Validators - 使用Tendermint共识协议，这是一种部分同步的拜占庭容错(BFT)共识算法。每个验证者节点都可以对区块提案进行投票，其投票权与绑定/委托的质押币(ZETA)成正比。每个验证者节点由其共识公钥标识。验证者需要一直在线，准备好参与不断增长的区块生产。作为服务的交换，验证者将获得区块奖励和交易费。</p></li><li><p>Observers - 通过外部链的完整节点观察外部连接链在特定地址的某些相关交易/事件/状态。观察者将分为两个角色：排序者和验证者。序列器发现相关的外部事务/事件/状态并向验证者报告；验证者对ZetaChain进行验证和投票以达成共识。该系统至少需要一个序列器和多个验证器。</p></li><li><p>TSS(Threshold Signature Scheme) signer - ZetaChain共同持有标准的ECDSA/EdDSA密钥，用于与外部链进行身份验证交互。密钥在多个签名者之间分发，这样只有超级多数签名者才能代表ZetaChain签名。重要的是要确保任何时候都没有任何单个实体或一小部分节点能够代表ZetaChain在外部链上签署消息。ZetaChain系统使用担保质押和正/负激励来确保经济安全。</p></li></ul><p>Observer通过不停的监控外部链的ZETA代币的销毁、铸造、特定的消息以及智能合约的调用等事件，例如当其观察到一笔跨链转账交易时，会构造一笔入站消息<code>MsgVoteOnObservedInboundTx</code> ，该交易包含源链、目标链、发送者、接受者以及金额等字段；然后通过TSS signer签名后发往ZetaChain；ZetaChain 中的Observer对到来的<code>MsgVoteOnObservedInboundTx</code>进行校验并投票；投票通过后会生成一个跨链交易CCTX；外部Observer通过观察到ZetaChain 中生成的CCTX，根据CCTX中的参数，TSS signer构造目标链的交易(例如转账交易)签名后发往目标链，并记录下该交易的txID；Observer按时查询目标链上的txID，如果该交易已被目标链确认且状态为成功则会构造一笔出站交易<code>MsgVoteOnObservedOutboundTx</code> ，交易包含目标链的交易哈希等字段，交易通过TSS signer签名后发往ZetaChain网络；同样这笔出站交易需要Observer进行投票，投票通过后即被标记为交易成功，即完成了一笔跨链交易。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">模块介绍</h1><p>前面说过，ZetaChain在Cosmos-SDK的基础和原有模块的基础上添加了<code>crosschain</code>，<code>emissions</code>，<code>fungible</code>和<code>observer</code>四个模块。下面主要介绍各个模块主要的功能。</p><h2 id="h-crosschain" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">crosschain</h2><p><code>crosschain</code>模块跟踪入站和出站跨链事务(CCTX)，与<code>crosschain</code>模块交互的主要参与者是观察者验证器。观察者运行一个链下程序(称为<code>zetaclient</code>)，该程序监视连接的区块链上的入站交易，监视ZetaChain上的待处理的出站交易，并监视连接的链上的出站交易。在观察入站或出站事务之后，观察者参与投票过程。将选票移动到“finalized”状态的最后一次投票触发执行并支付跨链交易的gas成本。</p><h3 id="h-inbound-transaction" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Inbound Transaction(入站事务)</h3><p>入站事务是在连接链上观察到的跨链事务，比如一个连接链上的账户与该链上的跨链合约交互并发且了一笔跨链交易，Observer通过事件观察到这笔交易在连接链上已经被确认之后，会通过TSS Signer构造入站消息<code>MsgVoteOnObservedInboundTx</code> ，并且广播到ZetaChain，并且由ZetaChain上的Observer校验消息内容后进行投票。其中<code>MsgVoteOnObservedInboundTx</code> 的结构如下：</p><pre data-type="codeBlock" text="message MsgVoteOnObservedInboundTx {
    string creator = 1;                // 消息创建者，即Observer
    string sender = 2;                 // 交易发起者，即连接链上的账户或者合约地址
    int64 sender_chain_id = 3;         // 发起跨链交易的链的ID
    string receiver = 4;               // 跨链交易的接受者，可以是合约/账户地址
    int64 receiver_chain = 5;          // 目标链
    string amount = 6;                 // 转账数量
    string message = 8;                // 跨链消息
    string in_tx_hash = 9;             // 发起链发起的交易ID
    uint64 in_block_height = 10;       // 发起链的该交易的区块高度
    uint64 gas_limit = 11;             // Gas限制
    common.CoinType coin_type = 12;    // 代币类型，目前包括Zeta, Gas, ERC20和Cmd四种
    string tx_origin = 13;
    string asset = 14;
}  
"><code>message MsgVoteOnObservedInboundTx {
    string <span class="hljs-attr">creator</span> = <span class="hljs-number">1</span><span class="hljs-comment">;                // 消息创建者，即Observer</span>
    string <span class="hljs-attr">sender</span> = <span class="hljs-number">2</span><span class="hljs-comment">;                 // 交易发起者，即连接链上的账户或者合约地址</span>
    int64 <span class="hljs-attr">sender_chain_id</span> = <span class="hljs-number">3</span><span class="hljs-comment">;         // 发起跨链交易的链的ID</span>
    string <span class="hljs-attr">receiver</span> = <span class="hljs-number">4</span><span class="hljs-comment">;               // 跨链交易的接受者，可以是合约/账户地址</span>
    int64 <span class="hljs-attr">receiver_chain</span> = <span class="hljs-number">5</span><span class="hljs-comment">;          // 目标链</span>
    string <span class="hljs-attr">amount</span> = <span class="hljs-number">6</span><span class="hljs-comment">;                 // 转账数量</span>
    string <span class="hljs-attr">message</span> = <span class="hljs-number">8</span><span class="hljs-comment">;                // 跨链消息</span>
    string <span class="hljs-attr">in_tx_hash</span> = <span class="hljs-number">9</span><span class="hljs-comment">;             // 发起链发起的交易ID</span>
    uint64 <span class="hljs-attr">in_block_height</span> = <span class="hljs-number">10</span><span class="hljs-comment">;       // 发起链的该交易的区块高度</span>
    uint64 <span class="hljs-attr">gas_limit</span> = <span class="hljs-number">11</span><span class="hljs-comment">;             // Gas限制</span>
    common.CoinType <span class="hljs-attr">coin_type</span> = <span class="hljs-number">12</span><span class="hljs-comment">;    // 代币类型，目前包括Zeta, Gas, ERC20和Cmd四种</span>
    string <span class="hljs-attr">tx_origin</span> = <span class="hljs-number">13</span><span class="hljs-comment">;</span>
    string <span class="hljs-attr">asset</span> = <span class="hljs-number">14</span><span class="hljs-comment">;</span>
}  
</code></pre><p>如果跨链交易的目的链是ZetaChain，并且CCTX不包含消息，则ZRC20代币将存入ZetaChain上的帐户。</p><p>如果跨链交易的目标链是ZetaChain，并且CCTX包含消息，则ZRC20代币被存入ZetaChain，并调用ZetaChain上的合约。消息中包含合约地址和合约调用的参数。</p><p>如果跨链交易的目的链不是ZetaChain，则事务的状态更改为“待定出站”(pending outbound)，并将CCTX作为出站事务进行处理。</p><h3 id="h-outbound-transaction" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Outbound <strong>Transaction(出站事务)</strong></h3><p>与上面的入站事务相反，观察者通过观察跨链交易在目标链上完成之后，由TSS Signer构造出站消息<code>VoteOnObservedOutboundTx</code> 并发往ZetaChain，并且由ZetaChain上的Observer校验消息内容后进行投票。<code>VoteOnObservedOutboundTx</code> 的结构如下：</p><pre data-type="codeBlock" text="message MsgVoteOnObservedOutboundTx {
    string creator = 1;                               // 消息创建者
    string cctx_hash = 2;                             // 跨链交易哈希
    string observed_outTx_hash = 3;                   // 目标链上的交易哈希
    uint64 observed_outTx_blockHeight = 4;            // 目标链上的交易区块高度
    uint64 observed_outTx_gas_used = 10;              // Gas使用数量
    string observed_outTx_effective_gas_price = 11;   // 实际Gas价格
    uint64 observed_outTx_effective_gas_limit = 12;   // 实际Gas限制
    string value_received = 5;                        // 接收的代币数量
    common.ReceiveStatus status = 6;                  // 接收状态
    int64 outTx_chain = 7;                            // 目标链
    uint64 outTx_tss_nonce = 8;                       // TSS Nonce
    common.CoinType coin_type = 9;                    // 代币类型
}
"><code>message MsgVoteOnObservedOutboundTx {
    <span class="hljs-keyword">string</span> creator <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;                               <span class="hljs-comment">// 消息创建者</span>
    <span class="hljs-keyword">string</span> cctx_hash <span class="hljs-operator">=</span> <span class="hljs-number">2</span>;                             <span class="hljs-comment">// 跨链交易哈希</span>
    <span class="hljs-keyword">string</span> observed_outTx_hash <span class="hljs-operator">=</span> <span class="hljs-number">3</span>;                   <span class="hljs-comment">// 目标链上的交易哈希</span>
    <span class="hljs-keyword">uint64</span> observed_outTx_blockHeight <span class="hljs-operator">=</span> <span class="hljs-number">4</span>;            <span class="hljs-comment">// 目标链上的交易区块高度</span>
    <span class="hljs-keyword">uint64</span> observed_outTx_gas_used <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;              <span class="hljs-comment">// Gas使用数量</span>
    <span class="hljs-keyword">string</span> observed_outTx_effective_gas_price <span class="hljs-operator">=</span> <span class="hljs-number">11</span>;   <span class="hljs-comment">// 实际Gas价格</span>
    <span class="hljs-keyword">uint64</span> observed_outTx_effective_gas_limit <span class="hljs-operator">=</span> <span class="hljs-number">12</span>;   <span class="hljs-comment">// 实际Gas限制</span>
    <span class="hljs-keyword">string</span> value_received <span class="hljs-operator">=</span> <span class="hljs-number">5</span>;                        <span class="hljs-comment">// 接收的代币数量</span>
    common.ReceiveStatus status <span class="hljs-operator">=</span> <span class="hljs-number">6</span>;                  <span class="hljs-comment">// 接收状态</span>
    <span class="hljs-keyword">int64</span> outTx_chain <span class="hljs-operator">=</span> <span class="hljs-number">7</span>;                            <span class="hljs-comment">// 目标链</span>
    <span class="hljs-keyword">uint64</span> outTx_tss_nonce <span class="hljs-operator">=</span> <span class="hljs-number">8</span>;                       <span class="hljs-comment">// TSS Nonce</span>
    common.CoinType coin_type <span class="hljs-operator">=</span> <span class="hljs-number">9</span>;                    <span class="hljs-comment">// 代币类型</span>
}
</code></pre><h2 id="h-emissions" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">emissions</h2><p><code>emissions</code>模块负责协调观察者、验证者和TSS签名者的奖励分配。目前，它只向每个区块的验证者分发奖励。TSS和观察者的未分配数量存储在各自的池中。奖励的分配是在开始区块(Begin Blocker)中实现的。 该模块跟踪用于计算奖励的参数:</p><ul><li><p>Maximum bond factor</p></li><li><p>Minimum bond factor</p></li><li><p>Average block time</p></li><li><p>Target bond ratio</p></li><li><p>Validator emission percentage</p></li><li><p>Observer emission percentage</p></li><li><p>TSS Signer emission percentage</p></li><li><p>Duration factor constant</p></li></ul><h2 id="h-fungible" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">fungible</h2><p><code>fungible</code>模块有助于在ZetaChain上部署连接区块链的同质化代币(称为”foreign coins”)。外部链代币在ZetaChain上表示为ZRC20代币。当在ZetaChain上部署Foreign币时，将部署ZRC20合约，创建池，将流动性添加到池中，并将外部链币添加到模块状态的外部链币列表中。 该模块包含以下逻辑:</p><ul><li><p>在ZetaChain上部署外部链币</p></li><li><p>部署系统合约，Uniswap和包装ZETA</p></li><li><p>从连接的链中存入和调用ZetaChain上的全链智能合约(<code>DepositZRC20AndCallContract</code>和<code>DepositZRC20</code>)</p></li></ul><h2 id="h-observer" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">observer</h2><p><code>observer</code>模块跟踪投票的选票、链和观察者账户之间的映射、支持的连接链的列表、核心参数(合约地址、出站事务调度间隔等)、观察者参数(投票阈值、最小观察者委托等)和管理策略参数。</p><p>选票用于对入站和出站事务进行投票。观察者模块维护选票的创建、读取、更新和删除(CRUD)操作，以及确定选票是否已完成的辅助函数。投票系统由其他模块使用，例如当观察者验证器对事务进行投票时由跨链模块调用。</p><h1 id="h-cross-chain-message-passing" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">跨链消息传递(Cross-Chain Message Passing)</h1><p>一般说的跨链包括资产的跨链和消息的跨链，跨链桥一般只支持资产跨链，而ZetaChain提供消息跨链功能，应用可以基于消息跨链来开发各种跨链服务，包括资产跨链。因为ZetaChain跨链消息需要借助智能合约实现，所以像BitCoin等非智能合约链是无法实现消息跨链的（但是可以进行资产跨链）。同时，ZetaChain在对消息进行跨链的同时也能携带ZETA进行跨链。</p><p>应用要实现消息跨链传递，需要分别在源链和目标链上部署一个支持标准跨链接口的合约，然后通过调用合约的跨链消息函数进行跨链，此时ZetaChain充当一个中继的角色，负责将原链上消息转发到目标链上。</p><p>ZetaChain在每条连接它的链(EVM兼容链)上都部署了<code>ZetaInteractor</code>合约，该合约包含如下结构和接口：</p><pre data-type="codeBlock" text="interface ZetaInterfaces {
    struct SendInput {
        uint256 destinationChainId; // 目标链ID
        bytes destinationAddress;   // 目标链接收消息的地址
        uint256 destinationGasLimit; 
        bytes message;              // 要发送的消息
        uint256 zetaValueAndGas;    // 发送的ZETA的数量，会从中扣除一部分作为跨链Gas
        bytes zetaParams;           // 参数
    }
}
interface ZetaConnector {
    function send(ZetaInterfaces.SendInput calldata input) external;
}
"><code><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ZetaInterfaces</span> </span>{
    <span class="hljs-keyword">struct</span> <span class="hljs-title">SendInput</span> {
        <span class="hljs-keyword">uint256</span> destinationChainId; <span class="hljs-comment">// 目标链ID</span>
        <span class="hljs-keyword">bytes</span> destinationAddress;   <span class="hljs-comment">// 目标链接收消息的地址</span>
        <span class="hljs-keyword">uint256</span> destinationGasLimit; 
        <span class="hljs-keyword">bytes</span> message;              <span class="hljs-comment">// 要发送的消息</span>
        <span class="hljs-keyword">uint256</span> zetaValueAndGas;    <span class="hljs-comment">// 发送的ZETA的数量，会从中扣除一部分作为跨链Gas</span>
        <span class="hljs-keyword">bytes</span> zetaParams;           <span class="hljs-comment">// 参数</span>
    }
}
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ZetaConnector</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">send</span>(<span class="hljs-params">ZetaInterfaces.SendInput <span class="hljs-keyword">calldata</span> input</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
}
</code></pre><p>合约要发出跨链消息很简单，只需要继承<code>ZetaInteractor</code> 合约，然后在合约里面调用<code>ZetaConnector</code> 接口的<code>send</code>函数即可，要发送的跨链消息封装在<code>SendInput</code>结构体中。</p><p>在接收端（目标链），接收合约需要继承ZetaReceiver的接口，接收一个<code>ZetaMessage</code> 结构的跨链消息。</p><pre data-type="codeBlock" text="interface ZetaInterfaces {
struct ZetaMessage {
    bytes zetaTxSenderAddress;   // 发送者
    uint256 sourceChainId;       // 源链ID
    address destinationAddress;  // 目标链接收地址
    uint256 zetaValue;           // 发送的ZETA的数量
    bytes message;               // 具体消息
}
}
interface ZetaReceiver {
    function onZetaMessage(ZetaInterfaces.ZetaMessage calldata zetaMessage) external;
}
"><code><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ZetaInterfaces</span> </span>{
<span class="hljs-keyword">struct</span> <span class="hljs-title">ZetaMessage</span> {
    <span class="hljs-keyword">bytes</span> zetaTxSenderAddress;   <span class="hljs-comment">// 发送者</span>
    <span class="hljs-keyword">uint256</span> sourceChainId;       <span class="hljs-comment">// 源链ID</span>
    <span class="hljs-keyword">address</span> destinationAddress;  <span class="hljs-comment">// 目标链接收地址</span>
    <span class="hljs-keyword">uint256</span> zetaValue;           <span class="hljs-comment">// 发送的ZETA的数量</span>
    <span class="hljs-keyword">bytes</span> message;               <span class="hljs-comment">// 具体消息</span>
}
}
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ZetaReceiver</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onZetaMessage</span>(<span class="hljs-params">ZetaInterfaces.ZetaMessage <span class="hljs-keyword">calldata</span> zetaMessage</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
}
</code></pre><p>最终目标合约的<code>onZetaMessage</code> 函数会被调用用来接收跨链消息，应用可以在<code>onZetaMessage</code> 实现自己需要的逻辑，ZetaChain只负责发消息转发到发送者指定的合约。</p><p>此外，源链发送的跨链消息是可能会失败的，例如目标链的接收合约中的onZetaMessage函数对跨链消息进行校验失败。此时ZetaChain就需要将跨链消息发送的ZETA余额退回给发送者，并起告诉发送跨链消息的合约，这笔跨链消息失败了，以便源链的合约可以执行失败后对应的逻辑。所以源链合约还需要实现如下的接口：</p><pre data-type="codeBlock" text="interface ZetaInterfaces {
struct ZetaRevert {
    address zetaTxSenderAddress;
    uint256 sourceChainId;
    bytes destinationAddress;
    uint256 destinationChainId;
/// @dev Equals to: zetaValueAndGas - ZetaChain gas fees -
/// destination chain gas fees - source chain revert tx gas fees
uint256 remainingZetaValue;
    bytes message;
}
}
interface ZetaReceiver {
    function onZetaRevert(ZetaInterfaces.ZetaRevert calldata zetaRevert) external;
}
"><code><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ZetaInterfaces</span> </span>{
<span class="hljs-keyword">struct</span> <span class="hljs-title">ZetaRevert</span> {
    <span class="hljs-keyword">address</span> zetaTxSenderAddress;
    <span class="hljs-keyword">uint256</span> sourceChainId;
    <span class="hljs-keyword">bytes</span> destinationAddress;
    <span class="hljs-keyword">uint256</span> destinationChainId;
<span class="hljs-comment">/// @dev Equals to: zetaValueAndGas - ZetaChain gas fees -</span>
<span class="hljs-comment">/// destination chain gas fees - source chain revert tx gas fees</span>
<span class="hljs-keyword">uint256</span> remainingZetaValue;
    <span class="hljs-keyword">bytes</span> message;
}
}
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ZetaReceiver</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onZetaRevert</span>(<span class="hljs-params">ZetaInterfaces.ZetaRevert <span class="hljs-keyword">calldata</span> zetaRevert</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
}
</code></pre><p>如果跨链消息失败了，ZetaChain会调用源链合约的<code>onZetaRevert</code>函数，应用可以在该函数里面处理失败的逻辑，例如如果这是一笔跨链Swap交易，那么合约就需要将对应的Token退款给用户。</p><p>下面图片描述了跨链的过程，其中省略了ZetaChain内部的处理过程，暂时把它当成黑盒即可：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/28c4c6c5e21cde0abc96a2e68c5ddc34bae07d4c78cb5d8fbb00fb95858170fe.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>前面提到的跨链消息有一个很重要的应用就是开发跨链DEX，不过你可能也发现了，如果要使用跨链消息的话，需要在每个链都部署对应的合约，这会带来开发和维护上的花销，同时也会带来Gas上的额外花销和跨链等待的时间。那么还有没有什么其他方法可以更方便一点呢？答案是肯定的，这就是全链智能合约。ZetaChain上的全链合约指的是部署在ZetaChain上的智能合约，全链合约拥有如下功能：</p><ul><li><p>是任意可编程的，就像zEVM上的常规智能合约一样</p></li><li><p>直接管理外部链上的外部资产(同质化代币)</p></li><li><p>可以从外部链调用</p></li></ul><p>在zEVM上，智能合约可以直接托管这些外链资产，并像它们是本地的zEVM资产一样操纵它们。在外部链上，这些同质化代币由TSS地址控制； 在zEVM的内部，它们被表示为ZRC20——一个与ERC20兼容的合约，但具有处理存款和提款的接口用来管理ZetaChain的本地资产。任何在zEVM上的智能合约都可以通过与这些ZRC20合约交互而成为全链，并实现一个简单的接口，可以从外部链调用：</p><pre data-type="codeBlock" text="interface zContract {
    function onCrossChainCall(
        address zrc20,    // zrc20代币地址
        uint256 amount,   // 代币数量
        bytes calldata message  // 调用合约的信息
    ) external;
}
"><code><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">zContract</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onCrossChainCall</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> zrc20,    <span class="hljs-comment">// zrc20代币地址</span>
        <span class="hljs-keyword">uint256</span> amount,   <span class="hljs-comment">// 代币数量</span>
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> message  <span class="hljs-comment">// 调用合约的信息</span>
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
}
</code></pre><p>一个外部链调用ZetaChain的全链合约的步骤如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fccbe2613dd45c7f7427c8a01f64c3bc458594628b629ff8d0ed1d34eef23b15.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>用户将原生资产发送到链A上的TSS地址（对于ERC20，用户需要发送到由TSS地址控制的ERC20托管合约），并通过memo/消息指定<code>[zEVMContractAddress, message]</code> ，其中<code>zEVMContractAddress</code> 即为要交互的全链合约地址，<code>message</code> 为调用的消息。</p></li><li><p>ZetaClient 中的观察者观察到该调用消息，并将它报告给ZetaCore。</p></li><li><p>ZetaCore调用<code>SystemContract</code>中的<code>depositAndCall()</code>函数，<code>depositAndCall()</code>函数内部会再调用<code>zEVMContractAddress</code> 中的<code>onCrossChainCall()</code> 函数，其调用参数为：</p><ol><li><p>zrc20 - 用户在步骤1中发送的zrc20代币的合约地址</p></li><li><p>amount - 用户在步骤1中发送的zrc20合约代币的数量</p></li><li><p>message - 用户在步骤1中交易memo携带的消息</p></li></ol></li><li><p>应用合约应该在<code>onCrossChainCall()</code> 函数中实现自己的逻辑并处理调用。如果此时没有报错则该交易执行完成。</p></li><li><p>如果在调用<code>onCrossChainCall()</code> 或者之前的步骤执行失败，系统内部会创建一个CCTX(跨链事务)并将其标记为失败，最终扣除用户的Gas费用后将用户在步骤1中发送的剩余代币返还给用户。</p></li></ol><p><code>SystemContract</code> 合约中的<code>depositAndCall()</code>函数实现如下：</p><pre data-type="codeBlock" text="function depositAndCall(
        zContext calldata context,
        address zrc20,
        uint256 amount,
        address target,
        bytes calldata message
    ) external {
        if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule();
        if (target == FUNGIBLE_MODULE_ADDRESS || target == address(this)) revert InvalidTarget();
                // 调用转账的zrc20代币合约的deposit进行存款
        IZRC20(zrc20).deposit(target, amount);
                // 调用用户指定的合约的onCrossChainCall方法
        zContract(target).onCrossChainCall(context, zrc20, amount, message);
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">depositAndCall</span>(<span class="hljs-params">
        zContext <span class="hljs-keyword">calldata</span> context,
        <span class="hljs-keyword">address</span> zrc20,
        <span class="hljs-keyword">uint256</span> amount,
        <span class="hljs-keyword">address</span> target,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> message
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> FUNGIBLE_MODULE_ADDRESS) <span class="hljs-keyword">revert</span> CallerIsNotFungibleModule();
        <span class="hljs-keyword">if</span> (target <span class="hljs-operator">=</span><span class="hljs-operator">=</span> FUNGIBLE_MODULE_ADDRESS <span class="hljs-operator">|</span><span class="hljs-operator">|</span> target <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)) <span class="hljs-keyword">revert</span> InvalidTarget();
                <span class="hljs-comment">// 调用转账的zrc20代币合约的deposit进行存款</span>
        IZRC20(zrc20).deposit(target, amount);
                <span class="hljs-comment">// 调用用户指定的合约的onCrossChainCall方法</span>
        zContract(target).onCrossChainCall(context, zrc20, amount, message);
    }
</code></pre><p><br>这里“全链”的概念实际上就是将已经连接到ZetaChain的外部链所有的资产都映射到了ZetaChain中的ZRC20中，即使是比特币这种不支持智能合约的链，也可以为它生成一个ZER20合约，这样比特币就可以在ZetaChain上和其他代币一起组成流动性池子。</p><p>那么，用户要怎么将上面转入ZetaChain中的资产提取到外部链上呢？其实很简单，ZRC20合约标准有一个<code>withdraw</code>函数，用户在ZetaChain调用这个函数就可以将资产提取到外部链。其合约实现如下：</p><pre data-type="codeBlock" text="function withdraw(bytes memory to, uint256 amount) external override returns (bool) {
        (address gasZRC20, uint256 gasFee) = withdrawGasFee();
        if (!IZRC20(gasZRC20).transferFrom(msg.sender, FUNGIBLE_MODULE_ADDRESS, gasFee)) {
            revert GasFeeTransferFailed();
        }
        _burn(msg.sender, amount);
        emit Withdrawal(msg.sender, to, amount, gasFee, PROTOCOL_FLAT_FEE);
        return true;
    }
function withdrawGasFee() public view override returns (address, uint256) {
        address gasZRC20 = ISystem(SYSTEM_CONTRACT_ADDRESS).gasCoinZRC20ByChainId(CHAIN_ID);
        if (gasZRC20 == address(0)) {
            revert ZeroGasCoin();
        }
        uint256 gasPrice = ISystem(SYSTEM_CONTRACT_ADDRESS).gasPriceByChainId(CHAIN_ID);
        if (gasPrice == 0) {
            revert ZeroGasPrice();
        }
        uint256 gasFee = gasPrice * GAS_LIMIT + PROTOCOL_FLAT_FEE;
        return (gasZRC20, gasFee);
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        (<span class="hljs-keyword">address</span> gasZRC20, <span class="hljs-keyword">uint256</span> gasFee) <span class="hljs-operator">=</span> withdrawGasFee();
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>IZRC20(gasZRC20).transferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, FUNGIBLE_MODULE_ADDRESS, gasFee)) {
            <span class="hljs-keyword">revert</span> GasFeeTransferFailed();
        }
        _burn(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount);
        <span class="hljs-keyword">emit</span> Withdrawal(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, to, amount, gasFee, PROTOCOL_FLAT_FEE);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdrawGasFee</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span>, <span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">address</span> gasZRC20 <span class="hljs-operator">=</span> ISystem(SYSTEM_CONTRACT_ADDRESS).gasCoinZRC20ByChainId(CHAIN_ID);
        <span class="hljs-keyword">if</span> (gasZRC20 <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>)) {
            <span class="hljs-keyword">revert</span> ZeroGasCoin();
        }
        <span class="hljs-keyword">uint256</span> gasPrice <span class="hljs-operator">=</span> ISystem(SYSTEM_CONTRACT_ADDRESS).gasPriceByChainId(CHAIN_ID);
        <span class="hljs-keyword">if</span> (gasPrice <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">revert</span> ZeroGasPrice();
        }
        <span class="hljs-keyword">uint256</span> gasFee <span class="hljs-operator">=</span> gasPrice <span class="hljs-operator">*</span> GAS_LIMIT <span class="hljs-operator">+</span> PROTOCOL_FLAT_FEE;
        <span class="hljs-keyword">return</span> (gasZRC20, gasFee);
    }
</code></pre><p>可以看到，该函数只是将用户要提取的代币销毁了而已。值得注意的是函数中有个Withdrawal事件，实际上ZetaChain内部在监听到该事件后，最终使用外部链的TSS账户将用户要提取的代币转给了用户。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">代币模型</h1><p>ZetaChain使用ZETA作为其原生代币，该代币会同步发行在所有连接到ZetaChain的链（比特币这种不支持智能合约的除外）。ZETA有两种作用，一是用作ZetaChain的Gas费，另一个是用作ZetaChain里和所有ERC20代币组成流动性池子。</p><p>首先说第一点，ZetaChain里面的转账、部署合约、合约交互等都需要消耗ZETA作为Gas；此外，ZETA还会用作跨链手续费，比如我要在A链将消息跨链到B链，此时就需要在A链发送跨链交易时顺便携带ZETA到交易中作为此跨链的Gas费。这个跨链手续费主要包含了ZetaChain里面跨链合约调用的手续费以及在目标链将消息发送给目标合约的Gas费。</p><p>为什么ZETA能作为目标链的手续费呢？这就要说第二点原因，ZetaChain里面会默认为每个接入的外部链的原生Gas代币和ZETA建立一个流动性池子（采用Uniswap合约），例如ETH/ZETA。当进行跨链时，用户支付的ZETA会通过该池子转换成该目标链对应的Gas，然后再将该代币进行销毁以用作外部链的Gas费。此外，所有部署到ZetaChain中的ZRC20合约都会自动和ZETA组成一个流动性池子。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b5ce5eb4626559443ec8e579855ca0415ba2a982d366db9f243600b33c19b3cc.png" alt="图片来源：ZetaChain" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">图片来源：ZetaChain</figcaption></figure><p>以下流程图展示了跨链消息传递如何使用这些将原生Gas(ZRC-20)与ZETA配对的核心池来支付出站事务。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/069269d4e5a9b215bbb7026863d7ad0fe798853b07d38fa4bda1406c8a4cfe11.png" alt="图片来源：ZetaChain" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">图片来源：ZetaChain</figcaption></figure><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">可拓展性</h1><p>前面说过，ZetaChain节点由ZetaCore和ZetaClient两个服务组成，并且他们是分别部署运行的(虽然之间有相互的依赖)，虽然他们的代码都放在了同一个仓库。通过对源码进行编译，可以生成<code>zetaclientd</code>和<code>zetacored</code>两个可执行文件，这两个服务通过网络通信进行信息交流而不是在同一个进程中。ZetaChain在拓展外部链时可以不用对ZetaCore进行升级，只需要对其配置参数进行更改，例如拓展其所支持链的配置列表等。但是需要对ZetaClient进行升级，添加对其新增外部链的Observer服务，用于监控该链的交易并生成对应的出入站事务消息。另外，对于新增链是EVM兼容链的情况，那么还会在它上面部署对应的必要的系统合约等合约用于执行跨链交易。总体来说可拓展性不用修改共识层，所以拓展不会太复杂，主要是接入链的风险方面需要注意。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">风险因素</h1><p>官方白皮书中列出了其认为的主要安全相关的因素，分别是：去中心化、出入站事务安全性、对无限铸币的综合防御和外部链受到攻击的防御，下面展开说明。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">去中心化</h2><p>共识层面的去中心化就不用多说了，去中心化程度取决于验证节点的数量以及分散程度。目前最大的中心化风险可能在于TSS(门限签名)的私钥管理，虽然官方说私钥会分散管理，但是当前并没有做到足够的透明，后续可能会引入治理来尽实现尽可能大的去中心化。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">出入站事务安全性</h2><p>ZetaClient通过连接到外部链的节点服务提供商或者全节点来观察外部链的事件，根据链上合约事件和ZetaCore进行交互，所以这里连接的外部节点服务商或全节点必须是值得信任的，这样才能保证外部信息源的可靠性。同时，所有入站出站事务都需要通过TSS、验证以及投票，投票的结果将会记录到链上，这部分是可以追溯的。假设以上都是可以保证的，那么链上的状态修改和安全就可以保证。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">对无限铸币的综合防御</h2><p>由于可以通过ZetaChain跨链移动的唯一原生代币是ZETA，并且ZetaChain实际上只管理从链A到链B的ZETA代币转移，所以Zetachain只需要保证ZETA不会被非法铸造，即防止通过攻击的方式增加跨链的ZETA总供应量。</p><p>ZetaChain通过以下方法来实现保护：</p><ol><li><p>ZetaChain节点将在启动ZETA代币的铸造之前检查跨链的总供应量。这可以防止ZetaChain节点软件中的软件错误或漏洞。</p></li><li><p>在链上的代币合约在铸造之前会检查跨链的ZETA的总供应量。ZETA的总供应量由Chainlink提供，并发布在每个连接的链上。这种保护确保了没有人可以随意铸造，而ZETA的总供应量在各个链之间保持不变。</p></li></ol><p>值得注意的是，如果攻击者控制了2/3的验证者，或者攻击者能够利用软件中的漏洞，他可以将另一个用户的合法铸造转移到他的钱包。在这种最坏的情况下，影响可以受到控制，因为攻击者只能在特定时间从活跃用户那里窃取，并且一旦用户注意到，系统将会被迅速停止。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">对外部链受到攻击的防御</h2><p>如果ZetaChain连接的外部链受到攻击，可能会导致以下违规行为：</p><ol><li><p>双花，导致ZETA代币供应量膨胀；</p></li><li><p>审查；</p></li><li><p>回滚导致跨链交易的原子性丧失，因为源链部分信息可能不再存在；</p></li><li><p>硬分叉，链分裂等等。</p></li></ol><p>ZetaChain的设计可以减缓其中一些情况，或者控制损害的无限扩散。例如，由于ZetaChain的总供应量检查，外部链导致无限铸造（通过反复回滚和支付）是不可能发生的。此外，使用ZETA代币进行所有跨链价值转移的dApps也受到保护，不会发生无限膨胀。对于其他受到攻击的外部链，ZetaChain应该进入紧急停机状态以评估情况。恢复将由利益相关者和治理机制协调进行。</p><p>所以外部链的审查显得格外重要，所有连接到ZetaChain的外部链应该是具有已经验证过的足够的安全性的，不然会给ZetaChain引入风险因素。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">其他因素</h2><p>上面讨论的是链系统本身的安全性，对于要在上面做开发的项目方和用户来说，我们还需要考虑其他因素，毕竟这关系到项目未来的发展和用户的资金安全问题，其他因素包括代码逻辑漏洞、团队、社区建设以及生态建设等。下面对这些因素做说明，读者可以自行评判。</p><p>先来说团队方面，根据网上公开的披露的信息，ZetaChain主要核心成员包括Ankur Nandwani(前coinbase、Brave、0x和Basic Attention Token的联合创始人)、Panruo Wu (THORchain的早期贡献者)和Brandon Truong(前buzzfeed、Udacity、Yada)。其他核心成员包括Cosmos、Ignite、Consensys和其他多个区块链项目的前雇员。团队核心成员基本都有较为丰富的去区块链领域相关经验，可以说是个足够专业化的团队。不过项目核心团队成员都比较低调，很少看到他们在社媒上公开对ZetaChain进行宣传或者和官媒进行互动，这一点看起来比较让人疑惑。</p><p>代码漏洞方面，由于ZetaChain本身的设计已经算足够复杂，随着接入的外部链的增多，系统只会变得越来越复杂，越复杂的系统就越容易出Bug。在接入外部链时，如果因为外部链新增的组件设计不合理导致漏洞的话，这将会直接影响到ZetaChain的安全。这无疑会引进一些不确定的因素。</p><p>社区建设方面，目前官方DC频道有超过80W的成员，推特关注数同样超过80万，这在区块链领域无疑已经是非常亮眼的数字，当然不能只通过社交媒体来判断项目的好坏。社区氛围个人体验还不错，会有人专门回复开发者提出的问题。</p><p>生态建设方面，根据官网的展示，目前接入ZetaChain的生态并不算多，也还没有较为出圈的项目出现，整体看起来生态还比较早期。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">使用场景</h1><p>适合接入ZetaChain的使用场景有：</p><ul><li><p>智能合约管理的外部资产</p></li><li><p>跨链AMM交易所</p></li><li><p>携带数据的跨链消息</p></li><li><p>多链NFT</p></li><li><p>通用支付</p></li><li><p>通用身份和资产</p></li><li><p>多链，多签金库</p></li><li><p>全链账户抽象或智能合约钱包</p></li><li><p>全链DeFi</p></li><li><p>全链DAOs</p></li></ul><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">项目生态</h1><p>项目生态在前面有提到过，目前生态项目有一百多个，以比较重要的DeFi举例，目前共有20个，大部分为ZetaChain上的原生项目。生态建设是ZetaChain需要加强的地方。如果按照ZetaChain原先的计划在年底上线主网的话，显然现在的生态是不足的。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">总结</h1><p>ZetaChain 选择通过使用Cosmos-SDK作为基础层而不是从头开发，大大减少了开发量的同时也增加了安全性。同时其选择作为一个平台而不单单是一个跨链桥，这在当前的跨链赛道是比较少见的，当前跨链赛道还没有一个头部项目，也许ZetaChain会成为那一个。</p>]]></content:encoded>
            <author>lukema95@newsletter.paragraph.com (0xlukema)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/ee84c50fd6cd11dbdd81aaf69237676d4590d679fbb3184a485da80900a51256.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[账户抽象漫谈一：账户抽象赛道研究]]></title>
            <link>https://paragraph.com/@lukema95/pY6FhnRyE9lFbN2fUT2a</link>
            <guid>pY6FhnRyE9lFbN2fUT2a</guid>
            <pubDate>Tue, 26 Sep 2023 03:12:22 GMT</pubDate>
            <description><![CDATA[账户抽象赛道研究概述近日ERC-4337账户抽象基金公布了获得基金资助的18个项目，让账户抽象又一次进入了大家的视野。传统的EOA账户由于其在安全性，可定制化，便捷性和可拓展性等方面的不足，一直被人认为是阻挡大众进入区块链世界的一个门槛。EIP-4337提案在2021-09-29由Vitalik Buterin , Yoav Weiss , Dror Tirosh , Shahaf Nacson, Alex Forshtat, Kristof Gazso 和 Tjaden Hess 共同提出，至今已经过去两年，现在这个赛道已经有很多项目，不过大部分都还在早期阶段，还没有一个像是MetaMask一样杀手级别的项目出现。 ERC-4337的一些名词定义：UserOperation - 描述代表用户发送的事务的结构。为了避免混淆，它没有被命名为“交易”。 与交易一样，它包含“sender”、“to”、“calldata”、“maxFeePerGas”、“maxPriorityFee”、“signature”、“nonce” 与交易不同，其中“signature”字段的使用不是由协议定义...]]></description>
            <content:encoded><![CDATA[<h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">账户抽象赛道研究</h1><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">概述</h1><p>近日ERC-4337账户抽象基金公布了获得基金资助的18个项目，让账户抽象又一次进入了大家的视野。传统的EOA账户由于其在安全性，可定制化，便捷性和可拓展性等方面的不足，一直被人认为是阻挡大众进入区块链世界的一个门槛。EIP-4337提案在2021-09-29由Vitalik Buterin , Yoav Weiss , Dror Tirosh , Shahaf Nacson, Alex Forshtat, Kristof Gazso 和 Tjaden Hess 共同提出，至今已经过去两年，现在这个赛道已经有很多项目，不过大部分都还在早期阶段，还没有一个像是MetaMask一样杀手级别的项目出现。</p><p>ERC-4337的一些名词定义：</p><ul><li><p>UserOperation - 描述代表用户发送的事务的结构。为了避免混淆，它没有被命名为“交易”。 与交易一样，它包含“sender”、“to”、“calldata”、“maxFeePerGas”、“maxPriorityFee”、“signature”、“nonce” 与交易不同，其中“signature”字段的使用不是由协议定义的，而是由每个帐户实现定义的</p></li><li><p>Sender - 发送用户操作的账户合约。</p></li><li><p>EntryPoint - 执行 UserOperations 的单例合约。Bundlers/Clients将支持的EntryPoint列入白名单。</p></li><li><p>Bundler - 一个节点（块构建器），可以处理 UserOperations，创建有效的 EntryPoint.handleOps() 事务，并在其仍然有效时将其添加到块中。</p></li><li><p>Aggregator - 受账户信任的辅助合约，用于验证聚合签名。Bundlers/Clients将受支持的Aggregator 列入白名单。</p></li></ul><p>账户抽象的组件大概可以分为五类：Bundler, SDK, EntryPoint , Paymaster 和 SmartContract Account。其中Bundler 类似于节点的功能，对外提供RPC服务，接收UserOperation交易，并将交易验证打包后发往EntryPoint合约执行；SDK 面向开发者，用来生成交易，发送交易到Bundler 等；EntryPoint 是所有UserOperation交易的执行入口合约，EntryPoint 中会校验UserOperation的有效性，计算交易Gas，扣除Gas以及最终调用账户合约执行交易等操作；Paymaster 相当于一个存款合约，用户可以预先存款到 Paymaster，并在后面的交易中指定 Paymaster 来代付Gas；SmartContract Account 为用户的账户合约，里面会负责校验并执行从EntryPoint发来的交易，同时账户合约也是最灵活的合约，用户可以在遵循接口标准的前提下添加定制化的功能。</p><p>下面主要按照Bundler，SDK，一体化钱包，模块化钱包和零知识证明钱包进行类讨论相关的项目，最后再聊聊和ERC-4337相关的一些提案。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">赛道分类</h1><h2 id="h-bundler" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Bundler</h2><p>由于ERC-4337的定义了一个叫做<code>UserOperations</code>的交易数据结构，其和传统的由EOA钱包签署的交易是不兼容的，这就导致原来的节点内存池在不进行代码逻辑修改的情况下无法接收和验证该交易类型。为了不改变原来节点的交易模型和处理流程，ERC-4337引入了<code>Bundler</code>的概念，<code>Bundler</code>是一个能接收<code>UserOperations</code> 并进行验证和打包的节点。由此可知Bundler的重要行，所以很多老牌节点服务商已经在布局Bundler服务，同时也有很多新的项目开始专注于进行Bundler的开发，势必要在这个新赛道上分一杯羹。</p><h3 id="h-skandha-bundler" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Skandha Bundler</h3><p>Skandha 由 Etherspot 团队开发， 是一个完全符合ERC-4337标准的生产级Bundler，使用Typescript构建。Skandha 专注于对交易的Gas进行优化，它通过智能地将单个交易分组到捆绑包中来实现这一点，然后将捆绑包作为单个单元进行处理。通过将交易捆绑在一起，与单独执行每笔交易相比，Skandha减少了总Gas费用。</p><p>Skandha的一个关键特性是它能够处理多种交易类型，比如简单的转账、智能合约交互，甚至是批处理交易。这种灵活性允许开发人员有效地管理复杂的交易场景并优化他们的Gas使用。</p><p>此外，Skandha还提供了交易排序和优先级管理等高级功能。它允许开发人员指定交易执行的顺序，并分配优先级级别，以确保及时处理关键交易。</p><p>Skandha提供了两种不同的运行模式，Public和Private。Public版本与其他Bundler一样，显示所有公开验证的交易。Private版本可以选择对外隐藏白名单上的交易和钱包。</p><p><strong>Links:</strong> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bit.ly/43VN5TD">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherspot.fyi/?utm_source=ef_blog&amp;utm_medium=article&amp;utm_campaign=ef_grant">Dev Docs</a></p><h3 id="h-silius" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Silius</h3><p>Silius 是一个用Rust开发的Bundler，目前还在开发过程中，还有很多功能像P2P、签名聚合等都还未实现。Silius受到模块化以太坊节点实现方案<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://erigon.substack.com/p/architecture-of-erigon-separable">Erigon</a>的启发，将Bundler设计成多个模块，每个模块都能单独启动和运行。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2cce6b3516cb9630afaf26d4a9662de7007d56761bd73aa97d5d760c2e816a71.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>各个步骤的解释如下：</p><p>1.用户将来自兼容ERC-4337的钱包的<code>UserOperation</code>作为JSON-RPC请求发送到RPC API端点。他们还可以检索支持的Entry Point，Chain Id，估计用户操作Gas等。 2.ETH RPC API连接到UoPool，以向内存池添加新的<code>UserOperation</code>，检索支持的Entry Point，Chain Id，估计用户操作Gas等。 3.UoPool连接到ETH客户端以检索有关以太坊账户的信息(余额，股权/存款信息等)。ETH客户端应该启用调试名称空间，因为调用debug_traceCall是模拟用户操作所必需的。UoPool还连接到P2P网络中的其他Bundler，广播接收到的<code>UserOperation</code>并检索其他用户操作。 4.Bundler Core组件通过gRPC从UoPool检索用户操作，以创建<code>UserOperation</code> Bundles。 5.Bundler Core连接到ETH客户端获取有关账户和以太坊网络的信息。 6.Bundler Core组件将构建的bundle(普通的以太坊交易)发送到ETH客户端，一个区块生产者，或者将它们发送到Flashbots服务。在生产环境中，由于提前运行，不应该将bundle直接插入到公共tx池中。</p><p><strong>Links:</strong> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zerodev.app/">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/zerodev_app">Twitter</a></p><h3 id="h-stackup" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Stackup</h3><p>Stackup 团队是一个ERC-4337 基础设施开发团队，目前实现了一个用Go实现的模块化ERC-4337 Bundler。</p><p>Stackup Bundler 体系结构如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fdd386aedecf3127f5791951ef2deb595d9177650ba2aa924392cc19ab6576bd.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>UserOperation 通过任意传输层（例如 HTTP 或 P2P）到达客户端。</p></li><li><p>客户端验证 UserOperation：</p><p>2.1 检查 EntryPoint 是否受支持。 2.2 UseOperation 具有正确的架构。 2.3 内存池中没有重复项，或者它有足够高的气体来替换它。 2.4 通过中间件堆栈发送 UserOperation 进行处理。</p></li><li><p>UserOperation 被添加到内存池中。内存池还将其保存在嵌入式数据库中以进行持久化。</p></li><li><p>在单独的 goroutine 中，捆绑器将运行重复的过程：</p><p>4.1 查询内存池中的新批次。 4.2 通过中间件堆栈发送批次进行处理。 4.3 从内存池中删除已处理的 UserOperations。</p></li></ol><p>Links: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.stackup.sh/">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/stackup_fi">Twitter</a></p><h2 id="h-sdk" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">SDK</h2><h3 id="h-blocto-sdk" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Blocto SDK</h3><p>Blocto SDK旨在帮助开发人员以最小的努力构建出色的dapp。Blocto SDK 支持JavaScript SDK，Andriod SDK，iOS SDK 以及Unity SDK，并且支持大部分EVM兼容链。Blocto SDK 还与市面上大部分受欢迎的SDK进行了集成，例如 Web3.js，thirdweb 和 wagmi 等。</p><p>现在 Blocto SDK已经通过改造的 Web3.js 集成ERC-4337特定的RPC方法，例如 <code>sendUserOperation</code>、<code>estimateUserOperationGas</code>、<code>getUserOperationByHash</code>、<code>getUserOperationReceipt</code>和<code>supportedEntryPoints</code>。通过这样做，开发人员可以使用这些方法专注于他们真正关心的事情。例如，如果dApp想要提供更好的用户体验，则可以将调用方式从<code>sendTransaction</code> 更改 为<code>sendUserOperation</code>，只要钱包提供商支持此功能即可。</p><h3 id="h-biconomy" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Biconomy</h3><p>Biconomy Modular SDK 是一款专为去中心化应用程序 (dApp) 设计的综合软件开发套件。它建立在 ERC4337 解决方案之上，提供了从用户入门到持续参与的广泛解决方案，从而增强了 dApp 中的整体用户体验。通过利用模块化，该 SDK 提供了增强的定制、安全性和功能。</p><p>Biconomy SDK 以非托管方式为您的 dApp 带来与区块链无关、类似 web2 的体验。以下是目前启用的功能：</p><ul><li><p>轻松的用户入门：通过社交登录、帐户创建和恢复选项简化新用户的入门流程，将 web2 用户无缝集成到您的 dApp 中。</p></li><li><p>Fiat On Ramp：允许您的用户在您的 dApp 中轻松可靠地购买或出售加密货币，促进传统资产和基于区块链的资产之间的过渡</p></li><li><p>无 Gas 交易：为用户交互提供 Gas 费用，使其像 Web2 交易一样简单，并改善整体用户体验。</p></li><li><p>用ERC20代币支付Gas费：用户可以使用钱包中的任何ERC20资产来支付Gas费，提供灵活性和便利性。</p></li><li><p>自定义交易捆绑：使开发人员能够构建交易批处理方法，允许用户在单个交易中执行多个操作，甚至跨多个链。例如，用户可以在同一笔交易中批准和存款，而无需更改 dApp 智能合约中的任何内容。</p></li></ul><p>Biconomy SDK 要做的其实是类似ERC4337领域的Alchemy，ERC4337的基础设施服务。开发者通过调用SDK的函数可以快速创建一个ERC4337钱包，打包ERC4337交易并起发送到Bundler。此外Biconomy SDK还提供Paymaster 和 Bundler 服务，通过Biconomy Dashboard用户可以自己创建一个Paymaster 并进行白名单等管理，同时提供了 Bundler 的 API 服务。</p><p>Link: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.biconomy.io/">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/biconomy">Twitter</a></p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">一体化钱包</h2><h3 id="h-unipass" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">UniPass</h3><p>UniPass Wallet 是一个基于 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.wallet.unipass.id/zh/contract/intro">UniPass Contract</a> 开发的低门槛的支持邮件社交恢复的智能合约钱包解决方案。通过 UniPass Wallet，开发者可以在产品内提供流畅的免私钥、免 gas 的用户体验，从而快速地吸引海量的 Web2 用户。</p><p>UniPass的特色：</p><ul><li><p>免私钥-用户使用邮箱和密码注册、登录账户，无需接触助记词、私钥。</p></li><li><p>抗审查-用户完全掌握账户的控制权，无需依赖第三方服务。</p></li><li><p>免 Gas-支持使用任意链的任意资产支付交易手续费。</p></li><li><p>邮件恢复-用户可以通过守护者邮箱向链上合约提交账户恢复邮件，找回账户。</p></li><li><p>隐私保护-在链上采用零知识证明技术实现邮件脱敏验证，保护用户的邮箱隐私。</p></li><li><p>多平台-用户端覆盖网页版、移动端、浏览器插件全平台，支持多种调用方式。</p></li><li><p>支持多链-可以兼容所有 EVM 链，并且保证多链地址一致。</p></li></ul><p>UniPass Wallet 是一个对用户和开发者都是足够友好的钱包。对用户来说，目前支持直接使用邮箱和谷歌账户进行注册和登录，当密钥损毁或丢失时，用户可以通过选定作为守护者的多位互联网用户，通过向链上合约提交账户恢复邮件的方式，找回他们的账户，实现零门槛的社交恢复。对开发者来说，提供了Web，Android，iOS 等多个平台的 SDK 支持，使得开发者无论在移动端还是网页端都能快速的介入UnicPass。</p><p><strong>Links</strong>: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.unipass.id/">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/UniPassWallet">Twitter</a></p><h3 id="h-ambire" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Ambire</h3><p>Ambire 是一个通过DKIM进行自托管的电子邮件/密码认证的钱包，也就是说可以通过邮件和客户端密码的方式进行结合验证。</p><p>Ambire 智能帐户为多个签名者提供选项。支持两种类型的签名者。第一个是电子邮件/密码签名者，第二个是 EOA，例如硬件钱包、浏览器扩展（Web3 钱包）等。当使用电子邮件/密码签名者时，Ambire 会在后台创建一个 2/2 多重签名，其中一个密钥位于您的设备上并使用密码进行加密，而另一个则负责验证电子邮件验证码并存储在后端 HSM 上。这两把钥匙都是控制帐户所必需的，但一把就足以触发时间锁定恢复程序。</p><p>Ambre 钱包拥有以下特性：</p><ul><li><p>硬件钱包支持：Trezor、Ledger 和 Grid+ Lattice1</p></li><li><p>通过 WalletConnect 连接任何 dApp</p></li><li><p>自动交易费用管理</p></li><li><p>用稳定币支付交易费用</p></li><li><p>自动显示所有资产的仪表板：代币、NFT 和 DeFi 协议存款</p></li><li><p>多个 EVM 网络：Ethereum、Polygon、BSC、Avalanche 等等</p></li><li><p>在不影响Ambire自我管理特性的情况下注册一个电子邮件/密码</p></li><li><p>通过我们的入口合作伙伴用信用卡或银行转账存入FIAT</p></li><li><p>事务批处理:在一个事务中执行多个操作的能力</p></li><li><p>自动领先/三明治保护通过Flashbots</p></li><li><p>多个签名者(密钥)可以用来控制同一个账户:例如一个硬件钱包和一个软件钱包;可以很容易地启用或禁用这些功能</p></li></ul><p><strong>Links:</strong> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.ambire.com/wallet">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/AmbireWallet">Twitter</a></p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">零知识证明钱包</h2><h3 id="h-iotex" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">IoTeX</h3><p>IoTeX是一个将智能设备连接到区块链Dapps的web3平台，其团队的两名高级研究员开发了一个基于零知识证明的账户抽象钱包。钱包使用zkSNARK来确认帐户访问:如果用户拥有正确的密码，他们可以使用证明程序生成有效的证明来解锁帐户。然后链上智能合约验证此证明以验证交易。</p><p>简单看一下合约之中的校验逻辑正是用了零知识证明的校验，钱包合约中存储了用户密码的哈希passHash用来参与校验：</p><pre data-type="codeBlock" text="// https://github.com/iotexproject/zkp-wallet-contracts/blob/main/src/ZKPassAccount.sol
function _validateSignature(
        UserOperation calldata userOp,
        bytes32 userOpHash
    ) internal virtual override returns (uint256 validationData) {
        if (!verifyProof(userOp.signature, uint256(userOpHash))) {
            return SIG_VALIDATION_FAILED;
        }
        return 0;
    }

    function verifyProof(bytes calldata _proof, uint256 _opHash) public view returns (bool) {
        uint256[2] memory a;
        uint256[2][2] memory b;
        uint256[2] memory c;
        {
            (
                uint256 proof0,
                uint256 proof1,
                uint256 proof2,
                uint256 proof3,
                uint256 proof4,
                uint256 proof5,
                uint256 proof6,
                uint256 proof7
            ) = abi.decode(_proof[:256], (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256));
            a = [proof0, proof1];
            b = [[proof2, proof3], [proof4, proof5]];
            c = [proof6, proof7];
        }
        uint256 opProof = uint256(bytes32(_proof[256:]));

        _opHash %= SNARK_SCALAR_FIELD;

        uint256[4] memory input = [passHash, opProof, getNonce(), _opHash];
        return _verifier.verifyProof(a, b, c, input);
    }
"><code><span class="hljs-comment">// https://github.com/iotexproject/zkp-wallet-contracts/blob/main/src/ZKPassAccount.sol</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_validateSignature</span>(<span class="hljs-params">
        UserOperation <span class="hljs-keyword">calldata</span> userOp,
        <span class="hljs-keyword">bytes32</span> userOpHash
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> validationData</span>) </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>verifyProof(userOp.signature, <span class="hljs-keyword">uint256</span>(userOpHash))) {
            <span class="hljs-keyword">return</span> SIG_VALIDATION_FAILED;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyProof</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> _proof, <span class="hljs-keyword">uint256</span> _opHash</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">uint256</span>[<span class="hljs-number">2</span>] <span class="hljs-keyword">memory</span> a;
        <span class="hljs-keyword">uint256</span>[<span class="hljs-number">2</span>][<span class="hljs-number">2</span>] <span class="hljs-keyword">memory</span> b;
        <span class="hljs-keyword">uint256</span>[<span class="hljs-number">2</span>] <span class="hljs-keyword">memory</span> c;
        {
            (
                <span class="hljs-keyword">uint256</span> proof0,
                <span class="hljs-keyword">uint256</span> proof1,
                <span class="hljs-keyword">uint256</span> proof2,
                <span class="hljs-keyword">uint256</span> proof3,
                <span class="hljs-keyword">uint256</span> proof4,
                <span class="hljs-keyword">uint256</span> proof5,
                <span class="hljs-keyword">uint256</span> proof6,
                <span class="hljs-keyword">uint256</span> proof7
            ) <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(_proof[:<span class="hljs-number">256</span>], (<span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span>, <span class="hljs-keyword">uint256</span>));
            a <span class="hljs-operator">=</span> [proof0, proof1];
            b <span class="hljs-operator">=</span> [[proof2, proof3], [proof4, proof5]];
            c <span class="hljs-operator">=</span> [proof6, proof7];
        }
        <span class="hljs-keyword">uint256</span> opProof <span class="hljs-operator">=</span> <span class="hljs-keyword">uint256</span>(<span class="hljs-keyword">bytes32</span>(_proof[<span class="hljs-number">256</span>:]));

        _opHash <span class="hljs-operator">%</span><span class="hljs-operator">=</span> SNARK_SCALAR_FIELD;

        <span class="hljs-keyword">uint256</span>[<span class="hljs-number">4</span>] <span class="hljs-keyword">memory</span> input <span class="hljs-operator">=</span> [passHash, opProof, getNonce(), _opHash];
        <span class="hljs-keyword">return</span> _verifier.verifyProof(a, b, c, input);
    }
</code></pre><p>项目目前还有很多功能待完善，像邮件验证功能等。钱包未来愿景包括在账户抽象钱包的多个方面整合zkp，包括通过web2身份验证进行钱包操作，使用web2社交媒体状态进行社交恢复，以及使用流行的web2支付渠道初始化零余额钱包。</p><ol><li><p>Smart contracts - <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/iotexproject/zkp-wallet-contracts">https://github.com/iotexproject/zkp-wallet-contracts</a></p></li><li><p>ZKP circuits - <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/iotexproject/zkp-wallet-circuits">https://github.com/iotexproject/zkp-wallet-circuits</a></p></li></ol><p><strong>Links:</strong> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://iotex.io/">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/iotex_io">Twitter</a></p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">模块化钱包</h2><h3 id="h-zerodev-kernel" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">ZeroDev <strong><em>Kernel</em></strong></h3><p>ZeroDev Kernel 的设计理念类似于Linux Kernel, 就好比Linux Kernel可以被应用于Android、树莓派等操作系统一样，ZeroDev Kernel旨在构建一个最小的智能合约账户，这个智能合约账户可以很方便的被拓展，这样 Kernel 的设计者可以专注于 Kernel 的设计，其他开发人员只用在 Kernel 之上去进行拓展而不需要从头开始设计和开发整个AA钱包。</p><p>具体来说，内核包括以下我们认为对任何AA钱包必不可少的基本功能:</p><ul><li><p>兼容ERC-4337</p></li><li><p>用ERC-1271验证签名</p></li><li><p>批处理事务</p></li><li><p>委托调用(Delegating calls)</p></li></ul><p>此外，ZeroDev Kernel 为开发人员构建了一个插件框架，以便在内核之上添加功能，而无需升级帐户本身。Kernel的一个主要目标是培育一个繁荣的插件生态系统，Alchemy的开发人员最近起草了ERC-6900，题为“模块化智能合约账户和插件”（Modular Smart Contract Accounts and Plugins.）。EIP的目标是在智能合约账户(例如Kernel)和插件之间定义一个公共接口。ZeroDev Kernel团队声称到目前为止，Kernel是他们所知道的最接近ERC-6900的实现，一旦最终完成，ZeroDev Kerne团队将使Kernel与<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-6900">ERC-6900</a>完全兼容。所以为Kernel开发的插件不是只能在Kernel上工作。</p><p>大部分智能合约钱包会使用代理合约的方式来部署，这样后续可以对钱包合约进行升级而不用改变钱包合约的地址，但是在实际中对逻辑合约的升级需要遵守一些规则，最主要的原因是如果升级后的逻辑合约的存储设计不合理，会很容易导致新的升级合约的变量存储地址覆盖掉升级前的变量的存储地址，或者导致升级后存储地址的冲突。这就对开发人员有较高的要求。ZeroDev Kernel在设计时考虑了迁移。为此，Kernel使用钻石存储(<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://dev.to/mudgen/how-diamond-storage-works-90e"><em>diamond storage</em></a>)——一种确保一个钱包的数据存储不会与另一个钱包的数据存储发生冲突的技术。因此，从内核迁移到内核上构建的钱包是非常安全的。</p><p><strong>Links:</strong> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zerodev.app/">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/zerodev_app">Twitter</a></p><h3 id="h-rhinestone" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Rhinestone</h3><p>Rhinestone 是一个账户抽象模块化平台，Rhinestone 协议由几个关键组件组成，分别是Accounts、Modules、Module Registry 和 Adapters。其主要目标是成为一个可靠的中立中间件层，解决用户安全、帐户可移植性和帐户可扩展性问题。其架构设计如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/16647eb987a402b6a922d989d2cbe84111c457b91f7b846d0b3b5fd246f69975.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>其中：</p><ul><li><p>Account 是用作用户账户的智能合约（“smart accounts”）。这些智能帐户启用了模块。该协议与帐户无关，其目标是与所有模块化智能帐户实现进行互操作。</p></li><li><p>Modules 是扩展用户帐户功能的智能合约。模块分为四种不同的类型: 验证器(Validators)、执行器(Executors)、挂钩(Hooks)和恢复(recovery)，模块将兼容<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-6900">ERC-6900</a>。</p></li><li><p>Module Registry是模块市场的基础层。它的主要功能是执行标准和安全保证。重要的是，模块注册表是无所有者、无需许可且免费的，为开发人员、审计人员、供应商和用户在模块市场中提供了最大的灵活性。</p></li><li><p>Adapters 是帐户和模块注册表之间存在的通信层。该适配器允许供应商或用户指定他们的安全态势并确保模块满足一组规定的安全规则和准则(使用<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum-magicians.org/t/erc-7484-registry-adapters-for-smart-accounts/15434">ERC-7484</a>标准)。</p></li></ul><p>Rhinestone 为开发者提供了一整套框架和标准，开发者只需要专注于开发特定的模块功能，以实现定制化的需求。</p><p><strong>Links:</strong> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.rhinestone.wtf/">Website</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/rhinestonewtf">Twitter</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/rhinestonewtf">Github</a></p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">相关提案</h2><h3 id="h-erc-6900-modular-smart-contract-accounts-and-plugins" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">ERC-6900: Modular Smart Contract Accounts and Plugins</h3><p>ERC-6900 提案标准化了智能合约账户和账户插件，它们是允许智能合约账户内可组合逻辑的智能合约接口。该提案符合 ERC-4337，受到ERC-2535 的启发，定义更新和查询模块化功能实现的接口 。</p><p>该标准定义了一个模块化的智能合约帐户，能够支持所有符合标准的插件。这使得用户的数据具有更大的可移植性，并且插件开发人员不必选择支持特定的帐户实现。这些插件可以包含执行逻辑(execution logic)、验证方案(validation schemes)和挂钩(hooks)。验证方案定义了智能合约帐户将批准代表其采取的操作的情况，而挂钩则允许执行前和执行后控制。</p><p><strong>Link</strong>: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-6900">提案网址</a></p><h3 id="h-erc-7484-registry-adapters-for-smart-accounts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">ERC-7484: Registry Adapters for Smart Accounts</h3><p>该提案提出了一个用于模块化智能帐户的标准化注册表适配器(registry adapter)。该适配器允许帐户通过证明注册表查询和验证有关模块的安全证明。适配器负责查询注册表并正确处理返回值。</p><p>该提案仍处于草案阶段，可能会根据社区的意见进行重大更改。</p><p><strong>Link</strong>: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum-magicians.org/t/erc-7484-registry-adapters-for-smart-accounts/15434">提案网址</a></p><h3 id="h-erc-4972-name-owned-account" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">ERC-4972: Name-Owned Account</h3><p>该提案建议通过将每个人类可读身份链接到可由名称(Name)身份所有者控制的单个智能合约帐户来扩展名称服务（例如 ENS）的功能。</p><p>一个NOA（Name-Owned Account） 有：</p><ul><li><p>ERC-137 定义的人类可读名称；和</p></li><li><p>自有账户（NOA），这是一个智能合约账户，其地址由名称衍生而来；和</p></li><li><p>可以部署和操作所拥有帐户的名称的所有者。</p></li></ul><p>下图说明了 NOA、名称节点(Name Node)和名称所有者(Name Owner)之间的关系，所有权由名称服务保证。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f95a8fb8ccaa27396371d9523e7dad95f79077a4a0b56b8c285a666a0f62708c.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>Link</strong>: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-4972">提案网址</a></p><h3 id="h-eip-7377-migration-transaction" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">EIP-7377: Migration Transaction</h3><p>该EIP提出了这样的机制：提供一种嵌入协议中的机制，将 EOA 迁移到智能合约。</p><p>EOA账户经过那么多年的发展，目前有大量资产存放在 EOA 账户中。如果用户无法轻易将 EOA 账户中的所有资产手动迁移到新的智能合约地址，这势必阻碍ERC-4377的发展。</p><p>EIP-7377 提出了一种特殊的交易类型，普通用户只需要发起这种交易，就可以将账号里的资产迁移到智能合约钱包。</p><p><strong>Link</strong>: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7377">提案网址</a></p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">总结</h1><p>ERC-4337 一直被认为是以太坊钱包的未来，不过最近火热的Telegram钱包也让人们看到了另一种可能，毕竟目前区块链用户也就4亿，而Telegram每月有将近8亿的月活用户。好在这两者并不是竞争的关系，如果像Telegram， Discord，X等Web2公司能将用户吸引到Web3，那何尝不是一件好事。账户抽象目前正在蓬勃发展，不过要满足易操作性，安全性，可拓展性等要求还需要一段时间。从当前的一些项目来看，能通过邮箱等Web2的方式进行登录似乎已经是各个ERC-4337钱包首要支持的基本功能。同时模块化的账户合约架构逐渐成为一些主流被接受，毕竟这也符合当前区块链架构的发展路径。而结合零知识证明来增加用户的隐私，也是一种未来钱包发展的一个重要方向。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">参考链接</h1><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://erc4337.mirror.xyz/hRn_41cef8oKn44ZncN9pXvY3VID6LZOtpLlktXYtmA">https://erc4337.mirror.xyz/hRn_41cef8oKn44ZncN9pXvY3VID6LZOtpLlktXYtmA</a></p>]]></content:encoded>
            <author>lukema95@newsletter.paragraph.com (0xlukema)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/83f99db811f266c46368eee53636745dd80ea0b7938fffb0445b3deef66d101b.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[全链游戏：谈谈Web游戏引擎]]></title>
            <link>https://paragraph.com/@lukema95/web</link>
            <guid>504uLXd1SU0wS9VCIiFr</guid>
            <pubDate>Tue, 26 Sep 2023 03:09:17 GMT</pubDate>
            <description><![CDATA[概述提起游戏引擎，大家最先想到的可能是Unity或者虚幻引擎这些在游戏领域早已耳熟能详的引擎。这些引擎功能强大，提供丰富的组件与图形渲染功能。Web3的游戏引擎的概念可能和Web2的有些差别(至少目前来说)，Web3的游戏框架主要解决的问题是如何提供一套框架和套件，使得开发人员可以快速方便的构建一个逻辑和数据都在链上，并且方便拓展的链上游戏(或者自治世界)，它们并不提供图形引擎。 PSE Trading将全链游戏的产业链划分为四个关键层次，与 Web2 游戏领域的层次相呼应：基建层、中间件层、服务 / 工具层和应用 / 游戏层。[图片来自https://foresightnews.pro/article/detail/41671] 目前比较知名的Web3游戏引擎有Dojo和MUD。虽然都习惯称Dojo和MUD为链上游戏引擎，不过可能称之为开发框架更为准确。下文也将主要分析这两个引擎的设计理念和提供的功能。Autonomous Worlds注意到在上图的应用层中的组件被标记为「自治世界」（Autonomous Worlds），这个名词很重要，可以说为Web3游戏引擎的设计提供了指导...]]></description>
            <content:encoded><![CDATA[<h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">概述</h1><p>提起游戏引擎，大家最先想到的可能是Unity或者虚幻引擎这些在游戏领域早已耳熟能详的引擎。这些引擎功能强大，提供丰富的组件与图形渲染功能。Web3的游戏引擎的概念可能和Web2的有些差别(至少目前来说)，Web3的游戏框架主要解决的问题是如何提供一套框架和套件，使得开发人员可以快速方便的构建一个逻辑和数据都在链上，并且方便拓展的链上游戏(或者自治世界)，它们并不提供图形引擎。</p><p>PSE Trading将全链游戏的产业链划分为四个关键层次，与 Web2 游戏领域的层次相呼应：基建层、中间件层、服务 / 工具层和应用 / 游戏层。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ab5407128f8bc9e044eeb986995ae5b6c4a35f00532e658c3005e5333d4de9fe.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>[图片来自<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://foresightnews.pro/article/detail/41671">https://foresightnews.pro/article/detail/41671</a>]</p><p>目前比较知名的Web3游戏引擎有Dojo和MUD。虽然都习惯称Dojo和MUD为链上游戏引擎，不过可能称之为开发框架更为准确。下文也将主要分析这两个引擎的设计理念和提供的功能。</p><h3 id="h-autonomous-worlds" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Autonomous Worlds</h3><p>注意到在上图的应用层中的组件被标记为「自治世界」（Autonomous Worlds），这个名词很重要，可以说为Web3游戏引擎的设计提供了指导思想。该词最先出自 Lattice（后续会谈到）的文章，文章中对其定义为：「由区块链执行的实体和客观引入规则系统。从某种意义上说，自治世界是相互客观的现实」。同时也谈到：</p><p>「自主世界具有明确的硬叙事边界、形式化的引入规则，不需要特权个体来维持世界的存在」。我们可以理解为自治世界就是一个任何人都可以参与（包括参与构建）、开放、没有明确规则（意味着每个人都可以定义自己的一套规则），没有人能完全掌握话语权的客观世界。这同时也作为全链游戏的另一种诠释。</p><p>Dojo在其文档中认为游戏必须至少具备以下两个基本特征才能被视为自治世界：</p><ol><li><p>分散的数据可用性层：虽然状态执行可能驻留在集中层上，但如果执行层不复存在，则可以重建状态至关重要。Rollups 提供了一种解决方案，提供增强的容量执行层，同时确保数据永久存储在以太坊上。这保证了世界的永久存在。</p></li><li><p>扩展世界的无许可入口点：世界合约必须能够在不需要许可的情况下接受新的系统和组件。虽然这并不意味着每个组件和系统都会被利用，但它们必须遵守这种模式，确保对潜在增强功能的开放和不受限制的访问。</p></li></ol><h3 id="h-escarc" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">ESC和ARC模型</h3><p>用于构建游戏引擎的一种常见设计模式是ESC（Entity Component System）模型。其中：</p><ul><li><p><strong>Entity（实体）-</strong> 代表了游戏中的对象或角色，它们通常是一个抽象的容器，可以附加不同的组件。</p></li><li><p><strong>Component（组件）-</strong> 代表了实体的特定属性或功能，比如位置、渲染器、碰撞器等。组件是可以重复使用的模块，它们可以附加到不同的实体上。</p></li><li><p><strong>System（系统）-</strong> 系统处理特定类型的组件，它们控制实体和组件之间的交互。系统在游戏循环中运行，负责更新组件的状态和处理各种游戏逻辑。</p></li></ul><p>这种模式的优势在于它将游戏对象分解成更小的、可重用的部分，使得游戏开发更加灵活和可扩展。同时，ESC 模型也有助于提高游戏性能，因为系统只处理特定类型的组件，而不需要遍历所有实体。ESC 模型通常与面向数据的设计一起使用，以实现高效的游戏开发。目前包括Dojo在内的很多框架都是参考的ERC模型。</p><p>下面是一个简单的例子用来方便理解：</p><pre data-type="codeBlock" text="
// 一个实体
Entity player {
}

// 定义组件
Component Position {
    float x
    float y
}

// 一个系统来处理位置更新
System MovementSystem {
    position = entity.get(Position)
      position.x += 1
    position.y += 1
}
"><code>
<span class="hljs-comment">// 一个实体</span>
Entity player {
}

<span class="hljs-comment">// 定义组件</span>
Component Position {
    float x
    float y
}

<span class="hljs-comment">// 一个系统来处理位置更新</span>
System MovementSystem {
    position <span class="hljs-operator">=</span> entity.get(Position)
      position.x <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>
    position.y <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>
}
</code></pre><p>ARC（ActionRegistryCore）模型由Jump提出，下面引用其文章中的定义和解释：</p><p>ARC是一个受传统ECS架构启发的链上信息组织框架。与传统的 ECS 一样，ARC 的实体是组件的无数据容器，而组件是普通数据类型，没有可以“附加”到实体的行为。与 ECS 不同，ARC 具有可以针对特定组件执行的“操作”而不是“系统”。主要区别在于，传统 ECS 中的系统是围绕传统游戏中使用的基于循环的架构构建的，而动作捆绑则说明了区块链架构是基于推送的。这里概述的 ARC 的具体实现是针对 Solana 生态系统的，但类似的架构也可以用于其他生态系统。ARC 的基本结构是三层洋葱。首先，核心负责维护注册管理机构和实体。其次，您有各种注册表合同，用于维护组件和操作的注册以及<em>治理</em>特征。最后，您拥有修改组件的（可选）游戏或操作合约。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/281474c2b8c0973712987eafbd6753145bf43147126075bba2ab446e683dfc5a.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>这里不对ARC做过多展开，想对其有更多了解的可以<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://jumpcrypto.com/writing/introduction-to-arc/">查看原文</a>。</p><h1 id="h-web3" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Web3游戏引擎</h1><h2 id="h-mud" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">MUD</h2><p>MUD（这个名字借用了 Multi-user dungeon 的首字母缩略词）以太坊上的游戏开发框架，也可以说是链上游戏开发框架的开创者，由 Lattice 团队开发。Lattice 正是开发了大名鼎鼎的全链游戏&lt;黑暗森林&gt;(<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zkga.me/">Dark Forest</a>) 的团队。Lattice 在开发Dark Forest时遇到了很多困难，包括：智能合约中状态和逻辑的耦合导致逻辑升级困难，链和客户端之间缺乏同步可能导致游戏状态不一致，访问控制的不规范给试图创建自己的插件和客户端的潜在第三方开发者带来了问题。于是，Lattice 团队构思并开发了MUD框架，试图解决上述问题。使用MUD框架，Lattice团队仅用1.5个月就开发出了OPCraft，OPCraft 是一个完全链上的像素世界，可以把它当成链上版本的&lt;我的世界&gt;。OPCraft 也取得了不小的成功。</p><p>MUD框架解构开发程序的过程，通过不同的堆栈来实现对应的功能：</p><ul><li><p><strong>Store</strong>：链上数据库，应用程序的所有数据都存储在Store中。</p></li><li><p><strong>World</strong>: 调用的入口点，标准化的访问控制、升级和模块，相当于逻辑合约。</p></li><li><p><strong>Indexer</strong>：可以使用tRPC查询的索引器，快速查询应用的链上数据。</p></li></ul><p>从以上的组件可以看出，MUD使用Store和World将逻辑和存储进行了解耦，类似于后端中的数据库和服务端代码。传统的代理(Proxy)合约和钻石(Diamond)合约虽然能在一定程度上对逻辑和数据存储进行解耦，但是都需要遵循一些规范，这无疑对合约开发者提出了不小的要求。MUD的解耦顺利的解决了这个问题，开发人员在开发程序时只需要定义好表单的数据结构，然后使用MUD配套的工具就可以直接生成对应的Solidity合约代码，该合约代码拥有对数据进行CURD的方法。</p><p>MUD中的Store存储整体上要比原生的存储节省一些Gas(当然Store中存储的数据也是在链上的，只是对数据存储结构做了一些优化)。下面是一个定义的表单的示例：</p><pre data-type="codeBlock" text="// table definition
Allowance: {
  keySchema: {
    from: &quot;address&quot;,
    to: &quot;address&quot;,
  },
  valueSchema: {
    amount: &quot;uint256&quot;,
  },
}
"><code>// <span class="hljs-selector-tag">table</span> definition
Allowance: {
  keySchema: {
    <span class="hljs-selector-tag">from</span>: <span class="hljs-string">"address"</span>,
    to: <span class="hljs-string">"address"</span>,
  },
  valueSchema: {
    amount: <span class="hljs-string">"uint256"</span>,
  },
}
</code></pre><p>通过MUD配套的命令行工具通过定义的表单文件直接生成对应的表单Solidity合约，然后就可以在World合约中通过如下的方法访问表单：</p><pre data-type="codeBlock" text="// storing
AllowanceTable.set(address(0), address(1), 10);
// getting
allowance = AllowanceTable.get(address(0), address(1));
"><code><span class="hljs-comment">// storing</span>
AllowanceTable.set(<span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>), <span class="hljs-keyword">address</span>(<span class="hljs-number">1</span>), <span class="hljs-number">10</span>);
<span class="hljs-comment">// getting</span>
allowance <span class="hljs-operator">=</span> AllowanceTable.get(<span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>), <span class="hljs-keyword">address</span>(<span class="hljs-number">1</span>));
</code></pre><p>Store和原生Solidity存储之间存在的一些差异:</p><ul><li><p>Store以与Solidity编译器不同的方式编码存储，从而节省具有多个数组的表的Gas。</p></li><li><p>Store不支持嵌套结构，每个表只允许14个动态类型(即:数组，字符串和字节列)。这与Solidity不同，Solidity支持嵌套结构体和无限数量的动态类型。</p></li><li><p>Store的数组上限为65,536个元素。然而，Store并没有限制每个表可以存储的记录数量。</p></li></ul><p>另一个重要概念是World，World其实就是一个入口点内核，负责协调不同合约对 Store 的访问，也就是说World是没有状态的，但是可以通过World来修改状态。Store的读写权限使用命名空间来进行控制，这里不展开，总的来说修改Store的合约会继承一个System合约，System可以读取任何表，并且它们对在其所在的同一命名空间下注册的表具有默认的写访问权限。为了写入不同命名空间中的表，需要由拥有相应命名空间的地址授予显式访问权限。</p><p>此外，World和Diamond(或Proxy)之间的还有一个区别在于World允许在World上无许可地注册新的逻辑和表。后者需要管理员才能添加和升级逻辑。World有针对Store的存储访问管理，它们可以放宽这一要求，允许任何人向应用程序添加新逻辑和新表，而不会引入安全风险。这也就是为什么称之为World而不是类似Logic之类的词的原因，World一开始的设想就是为了建立一个开放自治世界，允许玩家可以在此之上添加拓展。</p><p>最后一个重要的概念就是Indexer，在开发程序时很多场景前端或者客户端都需要及时同步链上的数据，一般情况下开发人员会使用轮询或者监听链上事件来获取最新数据，当然也可以使用第三方的RPC服务来获取。有了Indexer，开发人员可以将Indexer配置成指向对应的Store。然后，MUD 客户端连接到索引器就可以直接获取到最新的 Store 中的数据，这样就无需考虑数据同步的问题啦。</p><p>此外MUD还提供了一些配套的命令行工具和SDK方便开发者使用。</p><h2 id="h-dojo" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Dojo</h2><p>Dojo是以太坊二层网络StarkNet上的游戏开发框架，也是目前StarkNet上最完善和最被广泛应用的框架。Dojo 的工具套件消除了构建链上游戏的基础设施复杂性，使开发人员能够专注于其游戏的开发。Dojo 利用 ECS（Entity Component System）作为其核心架构，有效地管理自治世界（Autonomous Worlds）的状态和行为。该模型围绕作用于Entry的系统，Entry是纯数据组件的集合。系统根据对这些组件的持久查询有效地确定要处理哪些实体。Dojo的设计理念其实和MUD有些类似（应该是参考了MUD的实现），主要是提供工具套件将链上的数据存储和逻辑进行解耦。</p><p>Dojo提供如下的套件：</p><ul><li><p>Torii - Starknet 索引器。Dojo 标准化了合约状态以反映传统的关系数据库，能够自动索引所有合约状态，并通过通过 GraphQL API 或 gRPC 公开这些状态，使开发人员能够轻松查询和检索数据。</p></li><li><p>Katana - 一个可定制的 StarkNet 开发网络。它的速度非常快，可以让您快速迭代游戏逻辑。</p></li><li><p>Sozo CLI - CLI管理工具。它使您能够创建、构建、测试和部署您的世界(World)。此外，您可以制作新的组件和系统并将它们注册到您的世界中。</p></li></ul><p>其中Sozo的工作流程如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fa5df5ff398fc3df76d0dbe05b26395e106c6e3373051efc80e76c1de0a89e65.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>Dojo使用了ECS模型作为其核心架构，其使用Sozo生成的项目目录结构如下：</p><pre data-type="codeBlock" text="src
  - components.cairo
  - systems.cairo
  - lib.cairo
Scarb.toml
"><code>src
  <span class="hljs-operator">-</span> components.cairo
  <span class="hljs-operator">-</span> systems.cairo
  <span class="hljs-operator">-</span> lib.cairo
Scarb.toml
</code></pre><p>其中的一些关键概念:</p><ul><li><p>World - World合约作为一个核心系统内核，作为启动和解决所有交互的基础。在这个内核中，合约被部署、注册和执行，通过允许客户端参与单个而不是可能的多个合约，从而简化了下游系统的流程。</p></li><li><p>Component - 定义世界结构的基础，封装了系统可能发生变化的状态，即 Component = Data。</p></li><li><p>System - System 支撑着你的世界的逻辑。虽然系统本质上是无状态的，但它们的主要作用是修改组件的状态，即 Systems = Logic</p></li><li><p>Entry - 在Dojo中，实体被视为世界中的主键值，可以将Component附加到其上。</p></li><li><p>Authorization - 组件的部署者是它的初始所有者。组件所有者能够授予<code>owner</code>和<code>writer</code>角色。只有<code>owner</code>可以授予系统<code>writer</code>角色，该角色允许系统更新组件。</p></li><li><p>Event - 事件在解码Dojo世界的动态方面起着关键作用。每当组件有更新时，World合约就会发出这些事件。</p></li></ul><p>当然，Dojo也提供了必要的SDK提供给客户端使用。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">总结</h1><p>除了 Dojo 和 MUD 之外，Argus 和 Curio 也是备受关注的游戏引擎，不过这两个项目都还处于比较早期的阶段。下面是IOSG Ventures对这四款引擎的总结对比：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/50ab40558ac4330b2906ed181a56dea9dd68097b23387b24bdaf6f2c43a68f4e.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>[图片来源<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://foresightnews.pro/article/detail/41671">https://foresightnews.pro/article/detail/41671</a>]</p><p>目前Web3游戏引擎主要还是参考了ECS模型，遵循的是数据和逻辑解耦思路并提供一系列方便开发的工具坊。这些引擎确实很大程度方便了链上游戏开发者，也提供了一个未来链上游戏引擎开发的思路。但是同时我们也要看到，像以太坊这中运行缓慢的链要在其之上构建全链游戏是很困难的，Rollup 或者一些其他TPS更高的主网可能更适合。</p><p>此外，构建全链游戏目前还有一些需要解决的问题，Paradigm在其论文&lt;**The Open Problems of Onchain Games**&gt;中也指出了无权限区块链的某些属性阻止了对主流游戏设计机制的支持:</p><ul><li><p><strong>Incomplete Information</strong> （不完全信息）- 许多游戏中的关键机制。现有的解决方案存在不可接受的缺陷(例如，DarkForest的加密战争迷雾演变成硬件挖矿竞赛)。</p></li><li><p><strong>Automation &amp; Collusion</strong>（自动化和勾结）- 根本无法阻止。bot不能与真正的玩家区分开来，玩家也不能保证是独一无二的。开发者必须创造出不会被bot meta或Sybil合谋破坏的游戏。</p></li><li><p><strong>Ticking -</strong> 区块链是由异步交易驱动的。大多数传统游戏都是围绕着独立于玩家互动的基于时间的游戏循环而创造。</p></li></ul><p>当前的Web3游戏引擎虽然算不上完美，但是也算是开了个好头，而且目前游戏引擎还没有一个处于统治地位的项目出现，这是一片蓝海，相信后面会有更多游戏引擎的项目出现，各个公链也会跟进设计出符合自己链的设计的游戏引擎。</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">参考链接</h1><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mud.dev/">https://mud.dev/</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.dojoengine.org/">https://book.dojoengine.org/</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.paradigm.xyz/2023/08/onchain-games">https://www.paradigm.xyz/2023/08/onchain-games</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://foresightnews.pro/article/detail/41671">https://foresightnews.pro/article/detail/41671</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://0xparc.org/blog/autonomous-worlds">https://0xparc.org/blog/autonomous-worlds</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://jumpcrypto.com/writing/introduction-to-arc/">https://jumpcrypto.com/writing/introduction-to-arc/</a></p>]]></content:encoded>
            <author>lukema95@newsletter.paragraph.com (0xlukema)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/d8afb4ef3a8fb69c8f2383f84f9c13b194197150083149c3744abecde7827738.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>