diff --git a/_posts/2023-07-10-ERC-6551.markdown b/_posts/2023-07-10-ERC-6551.markdown deleted file mode 100644 index 251d034..0000000 --- a/_posts/2023-07-10-ERC-6551.markdown +++ /dev/null @@ -1,439 +0,0 @@ ---- -layout: post -title: "【译】ERC-6551 代币绑定账户提案" -date: 2023-07-10 16:30:00 +0800 -categories: eip -permalink: /eip/6551 -tags: eip erc 6551 ---- - -| eip | 标题 | 描述 | 作者 | 讨论链接 | 状态 | type | 分类 | 创建时间 | 依赖 | -|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| -| 6551 | 非同质化代币绑定账户 | ERC-721代币所拥有的智能合约账户的接口和注册表 | Jayden Windle (@jaydenwindle)
Benny Giang
Steve Jang, Druzy Downs (@druzydowns)
Raymond Huynh (@huynhr), Alanah Lam
Wilkins Chung (@wwhchung)
Paul Sullivan (@sullivph) | [https://ethereum-magicians.org/t/non-fungible-token-bound-accounts/13030](https://ethereum-magicians.org/t/non-fungible-token-bound-accounts/13030) | 草稿 | Standards Track | ERC | 2023-02-23 | [155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md), [165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md), [721](https://github.com/ethereum/EIPs/blob/bb55940264223f7063e660687475185aaed8a215/EIPS/eip-721.md), [1167](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1167.md), [1271](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1271.md) | - - -## 摘要 - -该提案定义了一个系统,为每个[ERC-721](https://github.com/ethereum/EIPs/blob/bb55940264223f7063e660687475185aaed8a215/EIPS/eip-721.md)代币提供了一个智能合约账户。这些与代币绑定的账户允许ERC-721代币拥有资产并与应用程序进行交互,而无需对现有的ERC-721智能合约或基础设施进行更改。 - -## 动机 - -ERC-721标准推动了非同质化代币应用的爆发。一些值得注意的应用案例包括可繁殖的猫咪、生成艺术品和流动性仓位。 - -非同质化代币越来越成为链上身份的一种形式。这是从ERC-721规范中自然而然地延伸出来的——每个非同质化代币都有一个全球唯一的标识符,进一步地,具有独特的身份。 - -与其他形式的链上身份不同,ERC-721代币无法充当代理人或与其他链上资产关联。这种限制与许多现实世界中的非同质化资产形成鲜明对比。例如: - -- 一个角色扮演游戏中的角色,根据他们所采取的行动逐渐积累财产和能力 -- 一辆由许多可替代和不可替代的零部件组成的汽车 -- 一个由多个可互换资产组成的自动投资组合 -- 一张拳击通行会员卡,可以进入一个场所,并记录过去的互动历史 - -有几个提案试图赋予ERC-721代币拥有资产的能力。这些提案中的每一个都对ERC-721标准进行了扩展。这要求智能合约作者在他们的ERC-721代币合约中包含提案支持。因此,这些提案在很大程度上与先前部署的ERC-721合约不兼容。 - -该提案赋予每个ERC-721代币与以前部署的ERC-721代币合约保持向后兼容的情况下,完全具备以太坊账户的全部功能。通过使用无需许可的注册表,为每个ERC-721代币部署独特的确定性寻址的智能合约账户。 - -每个代币绑定账户都由一个单独的ERC-721代币拥有,使得该代币能够与区块链进行交互,记录交易历史,并拥有链上资产。每个代币绑定账户的控制权被委托给ERC-721代币的所有者,使得所有者能够代表其代币发起链上操作。 - -代币绑定账户与几乎所有支持以太坊账户的现有基础设施兼容,包括链上协议和链下索引器。代币绑定账户可以拥有任何类型的链上资产,并可以扩展以支持未来创建的新资产类型。 - -## 规范 - -本文档中的关键词“必须”、“不得”、“要求”、“应当”、“不应”、“应该”、“不应该”、“建议”、“不建议”、“可能”和“可选”应按照RFC 2119和RFC 8174中的描述进行解释。 - -### 概述 - -本提案中概述的系统有两个主要组成部分: -- 一个无需许可的注册表,用于部署绑定代币账户 -- 代币绑定账户实现的标准接口 - -下图说明了ERC-721代币、ERC-721代币所有者、代币绑定账户和注册表之间的关系: - -![](/image/erc6551.png) - -### 注册 - -注册表作为希望使用代币绑定账户的项目的单一入口点。它具有两个功能: - -- `createAccount` - 给定 `implementation` 地址部署一个ERC-721代币绑定账户 -- `account` - 一个只读函数,根据 `implementation` 地址计算ERC-721代币绑定账户地址 - -注册表应将每个与代币绑定账户部署为一个[ERC-1167](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1167.md)最小代理,其中不可变的常量数据附加到字节码中。 - -每个代币绑定账户的部署字节码应具有以下结构: - -``` -ERC-1167 Header (10 bytes) - (20 bytes) -ERC-1167 Footer (15 bytes) - (32 bytes) - (32 bytes) - (32 bytes) - (32 bytes) -``` - -例如,具有实施地址 `0xbebebebebebebebebebebebebebebebebebebebe` 、盐值 `0` 、链ID `1` 、代币合约 `0xcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf` 和代币ID `123` 的代币绑定账户将具有以下部署的字节码: - -``` -363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf000000000000000000000000000000000000000000000000000000000000007b -``` - -每个代币绑定账户代理应该将执行委托给实现了 `IERC6551Account` 接口的合约。 - -注册合约是无需许可、不可变且没有所有者的。注册表的完整源代码可以在下面的[注册表实现](#注册表实现)找到。注册表应该在(待定)地址上部署,使用Nick的工厂合约( `0x4e59b44847b379578588920cA78FbF26c0B4956C` )和盐 `0x6551655165516551655165516551655165516551655165516551655165516551` 。 - -注册表应使用 `create2` 操作码部署所有绑定代币账户合约,以便每个ERC-721代币的账户地址是确定的。每个ERC-721代币的账户地址应由实现地址、代币合约地址、代币ID、[EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md)链ID和可选盐的唯一组合派生。 - -注册表应实现以下接口: - -```solidity -interface IERC6551Registry { - /// @dev The registry SHALL emit the AccountCreated event upon successful account creation - event AccountCreated( - address account, - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 salt - ); - - /// @dev Creates a token bound account for an ERC-721 token. - /// - /// If account has already been created, returns the account address without calling create2. - /// - /// If initData is not empty and account has not yet been created, calls account with - /// provided initData after creation. - /// - /// Emits AccountCreated event. - /// - /// @return the address of the account - function createAccount( - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 salt, - bytes calldata initData - ) external returns (address); - - /// @dev Returns the computed address of a token bound account - /// - /// @return The computed address of the account - function account( - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 salt - ) external view returns (address); -} -``` - -### 账户接口 - -所有代币绑定账户应通过注册表创建。 - -所有代币绑定账户的实现都必须实现[ERC-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md)接口检测。 - -所有代币绑定账户的实现都必须实现[ERC-1271](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1271.md)签名验证。 - -所有代币绑定账户的实现都必须实现以下接口: - -```solidity -/// @dev the ERC-165 identifier for this interface is `0x400a0398` -interface IERC6551Account { - /// @dev Token bound accounts MUST implement a `receive` function. - /// - /// Token bound accounts MAY perform arbitrary logic to restrict conditions - /// under which Ether can be received. - receive() external payable; - - /// @dev Executes `call` on address `to`, with value `value` and calldata - /// `data`. - /// - /// MUST revert and bubble up errors if call fails. - /// - /// By default, token bound accounts MUST allow the owner of the ERC-721 token - /// which owns the account to execute arbitrary calls using `executeCall`. - /// - /// Token bound accounts MAY implement additional authorization mechanisms - /// which limit the ability of the ERC-721 token holder to execute calls. - /// - /// Token bound accounts MAY implement additional execution functions which - /// grant execution permissions to other non-owner accounts. - /// - /// @return The result of the call - function executeCall( - address to, - uint256 value, - bytes calldata data - ) external payable returns (bytes memory); - - /// @dev Returns identifier of the ERC-721 token which owns the - /// account - /// - /// The return value of this function MUST be constant - it MUST NOT change - /// over time. - /// - /// @return chainId The EIP-155 ID of the chain the ERC-721 token exists on - /// @return tokenContract The contract address of the ERC-721 token - /// @return tokenId The ID of the ERC-721 token - function token() - external - view - returns ( - uint256 chainId, - address tokenContract, - uint256 tokenId - ); - - /// @dev Returns the owner of the ERC-721 token which controls the account - /// if the token exists. - /// - /// This is value is obtained by calling `ownerOf` on the ERC-721 contract. - /// - /// @return Address of the owner of the ERC-721 token which owns the account - function owner() external view returns (address); - - /// @dev Returns a nonce value that is updated on every successful transaction - /// - /// @return The current account nonce - function nonce() external view returns (uint256); -} -``` - -## 原理阐述 - -### 反事实帐户地址 - -通过指定一个规范的账户注册表,希望支持这个提案的应用程序可以在部署该账户的合约之前,使用特定的实现来计算给定代币绑定账户的地址。这样可以安全地将资产发送给代币的所有者,而无需知道所有者的地址。规范的注册表还允许客户端应用程序从单个入口点查询代币所拥有的资产。 - - -### 账户模糊不清 - -上述提议的规范允许ERC-721代币拥有多个绑定账户,每个实施地址一个。在制定这个提案的过程中,考虑了替代架构,该架构将为每个ERC-721代币分配一个单一代币绑定账户,使每个代币绑定账户地址成为一个明确的标识符。 - -然而,这些替代方案存在一些权衡。 - -首先,由于智能合约的无需许可性质,无法强制限制每个ERC-721代币只能绑定一个代币账户。任何希望在每个ERC-721代币上使用多个代币账户的人都可以通过部署额外的注册合约来实现。 - -其次,将每个ERC-721代币限制为一个与之绑定的账户将需要在该提案中包含一个静态、可信赖的账户实现。这个实现将不可避免地对代币绑定账户的能力施加特定的限制。鉴于该提案所能实现的未开发用例数量以及多样化账户实施对非同质化代币生态系统所带来的益处,作者认为在该提案中定义一个规范且受限的实施是过早的。 - -最后,这个提案旨在赋予ERC-721代币在链上充当代理的能力。在当前的实践中,链上代理通常会利用多个账户。一个常见的例子是个人使用一个“热”账户进行日常使用,而使用一个“冷”账户来存储贵重物品。如果链上代理通常使用多个账户,那么ERC-721代币应该具备相同的能力。 - -### 代理实现 - -ERC-1167最小代理在现有基础设施中得到了很好的支持,并且是一种常见的智能合约模式。该提案使用自定义的ERC-1167代理实现来部署每个代币绑定账户,该代理存储了盐值、实现地址、链ID、代币合约地址和代币ID,这些信息以ABI编码的常量数据附加到合约字节码中。这样可以让代币绑定账户的实现轻松查询这些数据,同时确保数据保持不变。采用这种方法是为了最大限度地兼容现有基础设施,同时在创建自定义代币绑定账户实现时给智能合约开发人员提供充分的灵活性。 - -### EIP-155 支持 - -该提案使用EIP-155链ID来识别ERC-721代币,同时包括其合约地址和代币ID。在单个以太坊链上,ERC-721代币标识符是全球唯一的,但在多个以太坊链上可能不唯一。使用链ID来唯一标识ERC-721代币,允许希望实施此提案的智能合约作者可选择支持多链代币绑定账户。 - -## 向后兼容 - -该提案旨在与现有的非同质化代币合约实现最大程度的向后兼容。因此,它不扩展ERC-721标准。 - -此外,该提案不要求注册表在创建账户之前执行 ERC-165 接口检查以确保与 ERC-721 的兼容性。这是有意设计的,以便最大限度地与早于ERC-721标准的非同质化代币合约(如Cryptokitties)保持向后兼容性。实施此提案的智能合约作者可以选择强制执行ERC-721的接口检测。 - -不实现 `ownerOf` 方法的非同质化代币合约(如Cryptopunks)与本提案不兼容。本提案中概述的系统可以通过进行轻微修改来支持此类收藏品,但这超出了本提案的范围。 - -## 参考实现 - -### 账户实施示例 - -```solidity -pragma solidity ^0.8.13; - -import "openzeppelin-contracts/utils/introspection/IERC165.sol"; -import "openzeppelin-contracts/token/ERC721/IERC721.sol"; -import "openzeppelin-contracts/interfaces/IERC1271.sol"; -import "openzeppelin-contracts/utils/cryptography/SignatureChecker.sol"; -import "sstore2/utils/Bytecode.sol"; - -contract ExampleERC6551Account is IERC165, IERC1271, IERC6551Account { - receive() external payable {} - - function executeCall( - address to, - uint256 value, - bytes calldata data - ) external payable returns (bytes memory result) { - require(msg.sender == owner(), "Not token owner"); - - bool success; - (success, result) = to.call{value: value}(data); - - if (!success) { - assembly { - revert(add(result, 32), mload(result)) - } - } - } - - function token() - external - view - returns ( - uint256 chainId, - address tokenContract, - uint256 tokenId - ) - { - uint256 length = address(this).code.length - return - abi.decode( - Bytecode.codeAt(address(this), length - 0x60, length), - (uint256, address, uint256) - ); - } - - function owner() public view returns (address) { - (uint256 chainId, address tokenContract, uint256 tokenId) = this - .token(); - if (chainId != block.chainid) return address(0); - - return IERC721(tokenContract).ownerOf(tokenId); - } - - function supportsInterface(bytes4 interfaceId) public pure returns (bool) { - return (interfaceId == type(IERC165).interfaceId || - interfaceId == type(IERC6551Account).interfaceId); - } - - function isValidSignature(bytes32 hash, bytes memory signature) - external - view - returns (bytes4 magicValue) - { - bool isValid = SignatureChecker.isValidSignatureNow( - owner(), - hash, - signature - ); - - if (isValid) { - return IERC1271.isValidSignature.selector; - } - - return ""; - } -} -``` - -### 注册表实现 - -```solidity -pragma solidity ^0.8.13; - -import "openzeppelin-contracts/utils/Create2.sol"; - -contract ERC6551Registry is IERC6551Registry { - error InitializationFailed(); - - function createAccount( - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 salt, - bytes calldata initData - ) external returns (address) { - bytes memory code = _creationCode(implementation, chainId, tokenContract, tokenId, salt); - - address _account = Create2.computeAddress( - bytes32(salt), - keccak256(code) - ); - - if (_account.code.length != 0) return _account; - - _account = Create2.deploy(0, bytes32(salt), code); - - if (initData.length != 0) { - (bool success, ) = _account.call(initData); - if (!success) revert InitializationFailed(); - } - - emit AccountCreated( - _account, - implementation, - chainId, - tokenContract, - tokenId, - salt - ); - - return _account; - } - - function account( - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 salt - ) external view returns (address) { - bytes32 bytecodeHash = keccak256( - _creationCode(implementation, chainId, tokenContract, tokenId, salt) - ); - - return Create2.computeAddress(bytes32(salt), bytecodeHash); - } - - function _creationCode( - address implementation_, - uint256 chainId_, - address tokenContract_, - uint256 tokenId_, - uint256 salt_ - ) internal pure returns (bytes memory) { - return - abi.encodePacked( - hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73", - implementation_, - hex"5af43d82803e903d91602b57fd5bf3", - abi.encode(salt_, chainId_, tokenContract_, tokenId_) - ); - } -} -``` - -## 安全考虑 - -### 防止欺诈 - -为了实现无需信任的代币绑定账户销售,去中心化市场将需要采取措施来防范恶意账户所有者的欺诈行为。 - -考虑以下可能的骗局: - -- Alice拥有一个ERC-721代币X,该代币拥有绑定账户Y -- Alice将10ETH存入Y账户 -- Bob提议通过一个去中心化市场以11ETH购买代币X,假设他将收到存放在账户Y中的10ETH和代币 -- Alice从绑定账户中提取了10ETH,并立即接受了Bob的报价 -- Bob收到了代币X,但账户Y是空的 - -为了减轻恶意账户所有者的欺诈行为,去中心化市场应在市场层面上实施对此类欺诈的保护措施。实施此EIP的合约也可以对欺诈行为实施一定的保护措施。 - -以下是一些应考虑的缓解策略: - -- 将当前代币绑定账户的nonce附加到市场订单上。如果账户的nonce自订单下达以来发生了变化,请视为该报价无效。此功能需要在市场层面上得到支持。 -- 在完成订单时,附上一份资产承诺清单,这些资产预计会保留在代币绑定账户中。如果自订单下达以来,任何已承诺的资产已从账户中移除,请视为该报价无效。这也需要由市场实施。 -- 通过外部智能合约将订单提交到分散市场,该合约在验证订单签名之前执行上述逻辑。这样可以实现安全的转账,无需市场支持。 -- 在代币绑定账户实现中,实施一个锁定机制,防止恶意所有者在账户被锁定时提取资产 - -防止欺诈行为超出了本提案的范围。 - -### 所有权循环 - -如果创建了所有权循环,所有存放在代币绑定账户中的资产可能会变得无法访问。最简单的例子是将ERC-721代币转移到其自己的代币绑定账户中。如果发生这种情况,ERC-721代币和存储在代币绑定账户中的所有资产将永久无法访问,因为代币绑定账户无法执行转移ERC-721代币的交易。 - -可以在任何具有n>0个代币绑定账户的图中引入所有权循环。由于需要无限的搜索空间,链上防止这些循环是很难强制执行的,因此超出了本提案的范围。鼓励应用客户和账户实施采纳此提案的措施,以限制所有权循环的可能性。 - -## 版权 - -版权和相关权利通过[CC0](https://github.com/ethereum/EIPs/blob/bb55940264223f7063e660687475185aaed8a215/LICENSE.md)放弃。 - ---- - -英文原版见:[https://eips.ethereum.org/EIPS/eip-6551](https://eips.ethereum.org/EIPS/eip-6551) \ No newline at end of file diff --git a/_posts/2023-07-29-Solidity-0.8.21.markdown b/_posts/2023-07-29-Solidity-0.8.21.markdown deleted file mode 100644 index 47f7b90..0000000 --- a/_posts/2023-07-29-Solidity-0.8.21.markdown +++ /dev/null @@ -1,114 +0,0 @@ ---- -layout: post -title: "【译】Solidity 0.8.21 发布公告" -date: 2023-07-29 10:30:00 +0800 -categories: solidity -permalink: /solidity/0821 -tags: solidity ---- - - -我们很高兴地宣布最新版本的 Solidity 编译器,[Solidity v0.8.21](https://github.com/ethereum/solidity/releases/tag/v0.8.21). - -## 显著的新功能 - -### 通过 IR 始终启用堆栈到内存的移动器 - -该版本解决了基于 IR 的代码生成流程产生未优化代码时过于容易出现“Stack Too Deep”错误的问题。这旨在帮助像调试器这样的工具,在处理优化代码时不再失去大部分效益。 - -传统的流水线即使在未经优化的模式下,通常也能避免耗尽可达的堆栈空间,但这是以更高的复杂性为代价的,因为许多优化都是作为代码生成的一部分硬编码的。新的流水线从一开始设计的目标就是将这两个问题分开。它产生的输出简单直接,但非常低效,旨在通过Yul优化器的离散步骤进行改进。每个步骤执行的转换可以在隔离的环境中进行验证和推理。这种方法的缺点是未经优化的代码往往有很多未使用的局部变量,并且会遇到堆栈问题。 - -我们的解决方案是通过最小的转换来处理未经优化的代码,以解决堆栈问题,但不会对其进行重大改变,就像完全优化那样。具体来说,编译器现在会对这样的代码运行 [`UnusedPruner`](https://docs.soliditylang.org/en/v0.8.21/internals/optimizer.html#unused-pruner) 优化步骤,以删除未使用的变量。此外,堆栈到内存移动机制现在始终处于启用状态,解决了剩余的堆栈问题,但会增加额外的内存访问开销。 - -这两件事情在现有的优化器设置中已经是可以实现的,但并不是默认行为的一部分。尽管在技术上涉及到优化器模块,但我们认为它们是新流程的重要组成部分,也是未经优化的代码的新基准 - -## 重要的错误修复 - -- 代码生成器:始终在传统代码生成流程中为 ``.selector`` 中的表达式生成代码。您可以在我们的[博客文章](https://soliditylang.org/blog/2023/07/19/missing-side-effects-on-selector-access-bug)中了解更多信息。 -- Yul 优化器:修复 ``FullInliner`` 步骤(``i``)在非表达式分割形式的代码中不保留传递给内联函数的参数的评估顺序(即在使用自定义优化器序列时,该步骤不在 ``ExpressionSplitter(x)``之前)。您可以在我们的[博客文章](https://soliditylang.org/blog/2023/07/19/full-inliner-non-expression-split-argument-evaluation-order-bug)中阅读更多相关信息。 - -## 语言特点 - -- 允许其他合约有资格访问事件。 -- 放宽对不可变变量初始化的限制。在函数和修饰器之外的构造时刻,现在可以随时进行读写操作。不再强制要求显式初始化。 - -### 放宽对不可变变量初始化的限制 - -鉴于过去几个月发现了一些错误,我们在不可变初始化检查方面的工作不够完善,从而导致了一些边缘情况,使得这些不可变对象可能保持未初始化状态(例如 ``try/catch`` 块的分支、 ``for`` 循环等),我们决定大幅放宽对不可变对象初始化的限制。 - -尽管不可变对象仍然只能在构造时显式初始化,但现在这种初始化是可选的,因为所有不可变对象都将被默认(零)初始化。我们还决定取消一次赋值限制,这意味着您现在可以对不可变对象进行多次赋值,最后一次赋值将被保留。 - -```solidity -contract C -{ - uint256 public immutable foo = 1; - - constructor() - { - foo = 2; - foo = 3; - } -} -``` - -然而,需要注意的是,我们放宽了这些限制,因为这些检查最终是为了保持合约创建的一致性而人为地强加了合约不可变性的限制,而不是出于安全原因。 -虽然仍然存在一些限制,比如在函数和修饰器中无法初始化不可变变量,但我们希望最终也能放开这些限制。 - -## 影响 IR 流水线中未优化字节码可重现性的错误 - -该版本修复了基于 IR 的代码生成器中的一个小错误,该错误导致根据编译器二进制文件的构建方式产生不同(但在功能上等效)的字节码。特别是,使用 Clang C++ 编译器构建的二进制文件在某些情况下会以不同于使用 GCC 构建的二进制文件的方式对 Yul 函数进行排序。触发该错误的情况是对内存数组进行索引。这将导致生成多个 Yul 辅助函数,其顺序取决于所使用的 C++ 编译器。 - -该错误仅影响由 IR 流水线生成的未优化代码,即需要在标准JSON中使用 `settings.viaIR` 或在 CLI 上使用 `--via-ir` 标志。使用 Yul 优化器可以抵消其影响,因为它会重新排序函数。 - -该错误影响了 [solc-bin](https://github.com/ethereum/solc-bin/) 中提供的官方二进制文件。emscripten 和 macOS 二进制文件使用 Clang 构建,Linux 二进制文件使用 GCC 构建,Windows 二进制文件使用 MSVC 构建。建议对合约进行源代码验证的工具考虑到合约可能由这些二进制文件之一生成。 - -## 完整更新日志 - -### 编译器功能 - -- 命令行界面:在汇编器模式下添加 ``--ast-compact-json`` 输出选项。 -- 命令行界面:为Solidity输入添加 ``--ir-ast-json`` 和 ``--ir-optimized-ast-json`` 输出,以提供 IR 和优化 IR 的紧凑JSON格式的 AST。 -- 命令行界面:在编译器模式下尊重 ``--optimize-yul`` 和 ``--no-optimize-yul``,并在汇编器模式下接受它们。现在,``--optimize --no-optimize-yul`` 的组合允许启用 EVM 汇编优化器而不启用 Yul 优化器。 -- EWasm:移除 EWasm 后端。 -- 解析器:引入 ``pragma experimental solidity``,这将启用一个实验性的语言模式,特别是在非破坏性发布之间没有稳定性保证,并且不适用于生产环境使用。 -- SMTChecker:添加 ``--model-checker-print-query`` 命令行选项和 ``settings.modelChecker.printQuery`` JSON 选项,以 SMTLIB2 格式输出 SMTChecker 查询。这需要仅使用 ``smtlib2`` 求解器。 -- 标准的 JSON 接口:为 Yul 输入添加 ``ast`` 文件级输出。 -- 标准的 JSON 接口:为Solidity输入添加 ``irAst`` 和 ``irOptimizedAst`` 合约级输出,为 IR 和优化后的 IR 提供紧凑的JSON格式的AST。 -- Yul 优化器:移除实验性的 ``ReasoningBasedSimplifier`` 优化步骤。 -- Yul优化器:在通过 IR 代码生成和纯 Yul 编译时,现在默认启用堆栈到内存的移动。 - -### 错误修复 - -- 代码生成器:禁止复杂表达式的结果为类型、内置函数、模块或某些不可赋值的函数。传统的代码生成流程实际上不会对它们进行评估,丢弃可能产生的任何副作用。 -- 代码生成器:修复未完全确定性的函数顺序在未优化的 Yul 输出中。在某些情况下,选择 C++ 编译器会导致不同(但等效)的字节码(特别是来自本机二进制文件与 emscripten 二进制文件)。 -- 命令行界面:修复在使用 ``--stop-after`` 解析并请求一些需要完整分析或编译的输出时出现的内部错误。 -- 命令行界面:不再允许同时指定 ``--optimize-yul`` 和 ``--no-optimize-yul``。 -- SMTChecker:在BMC引擎中修复 ``if`` 和 ``三元条件`` 语句中副作用的编码。 -- SMTChecker:修复当验证目标只能通过来自另一个公共函数的可信外部调用违反时的误报问题。 -- SMTChecker:修复在CHC引擎超时时,BMC引擎以可信模式进行外部调用时生成无效的 SMT-LIB2 脚本。 -- SMTChecker:修复由于错误地将使用函数指针的外部函数调用分类为公共获取器而导致的内部错误。 -- SMTChecker:修复由于使用外部标识符对接受内部函数作为参数的函数的成员访问进行编码而导致的内部错误。 -- 标准的 JSON 接口:修复在分析过程中由于某些致命错误而导致返回不完整的AST。 -- 类型检查器:禁止在复杂表达式中使用某些不可赋值的函数类型。 -- 类型检查器:引用不同声明的函数声明类型不再可互相转换。 -- Yul 优化器:确保将移至内存的变量的内存槽分配不依赖于可能取决于编译过程中是否包含其他文件的 AST ID。 -- Yul 优化器:修复 ``FullInliner`` 步骤未忽略非表达式分割形式的代码。 -- Yul 优化器:修复在生成字节码之前,优化的中间表示(IR)被不必要地再次通过 Yul 优化器传递的问题。 - -### AST变更 - -- AST: 在 ``SourceUnit`` 节点中添加 ``experimentalSolidity`` 字段,该字段指示是否通过 ``pragma experimental solidity`` 启用了实验性解析模式。 - -## 如何安装/升级 - -要升级到最新版本的 Solidity 编译器,只需按照我们文档中提供的[安装说明](https://docs.soliditylang.org/en/v0.8.21/installing-solidity.html)进行操作。我们的团队已经确保提供了详细而简单的步骤,以使升级过程尽可能顺利。如果在升级过程中遇到任何问题或困难,请随时联系我们的社区 [Matrix 频道](https://matrix.to/#/#ethereum_solidity:gitter.im)。 - -您可以在此处下载 Solidity 的新版本:[v0.8.21](https://github.com/ethereum/solidity/releases/tag/v0.8.21)。如果您想从源代码构建,请不要使用 GitHub 自动生成的源代码存档,而是请使用 [`solidity_0.8.21.tar.gz`](https://github.com/ethereum/solidity/releases/download/v0.8.21/solidity_0.8.21.tar.gz),并参考我们的[文档](https://docs.soliditylang.org/en/v0.8.21/installing-solidity.html#building-from-source)以获取构建指南。 - -请注意,对于使用 Solidity 版本低于 0.8.0, [there are breaking changes](https://docs.soliditylang.org/en/v0.8.21/080-breaking-changes.html) 的用户来说,有一些重大变更。我们强烈建议您查阅我们的文档中详细列出的重大变更列表,以确保平稳升级过程。此外,我们鼓励所有 Solidity 用户定期检查更新,以保持与最新的改进和优化保持同步。我们建议所有 Solidity 开发者升级到 0.8.21 版本,以享受这些改进和优化带来的好处。 - - ---- - - -> 原文链接:[https://soliditylang.org/blog/2023/07/19/solidity-0.8.21-release-announcement/](https://soliditylang.org/blog/2023/07/19/solidity-0.8.21-release-announcement/) \ No newline at end of file diff --git a/_posts/2023-11-08-solidity-0.8.23.md b/_posts/2023-11-08-solidity-0.8.23.md deleted file mode 100644 index 5d8d718..0000000 --- a/_posts/2023-11-08-solidity-0.8.23.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -layout: post -title: "【译】Solidity 0.8.23 发布公告" -date: 2023-11-08 22:30:00 +0800 -categories: solidity -permalink: /solidity/0823 -tags: solidity ---- - -> 原文链接:https://soliditylang.org/blog/2023/11/08/solidity-0.8.23-release-announcement/ - -今天,我们宣布发布了 Solidity 编译器 [v0.8.23](https://github.com/ethereum/solidity/releases/tag/v0.8.23 "https://github.com/ethereum/solidity/releases/tag/v0.8.23")。这个最新版本编译器旨在纯粹修复 bug,包括修复一个低严重性重要的 bug。 - -根据我们的调查,我们预见现实世界不会有该 bug 实例被用作漏洞利用或攻击向量,因此,我们评估其整体严重性为低。 - -这个版本还引入了一个小改变,优化器设置更加直观。自 v0.8.21 起,禁用 `optimizer.details.yul` 设置不再阻止编译器运行 [`UnusedPruner` 步骤](https://docs.soliditylang.org/en/v0.8.23/internals/optimizer.html#unused-pruner "https://docs.soliditylang.org/en/v0.8.23/internals/optimizer.html#unused-pruner"),我们认为这是防止堆栈问题的内部机制的一个重要部分。该步骤仍然可以被禁用 - 通过显式提供一个空的优化序列 - 但这需要名义上启用 Yul 优化器,这有时会导致用户启用整个优化器并无意中包含额外的优化。现在可以独立于其他设置使用空序列。 - -## 重要的 Bug 修复 - -### 修复无效的 `verbatim` 重复 bug - -用户报告了一个块重复 bug,导致除了 verbatim 指令内容不同之外相同的块被视为等效,因此合并为一个单一的块。新版本修复了这个 bug。 - -该 bug 自版本 `0.8.5` 存在,引入了 `verbatim`,并且只影响启用优化的纯 Yul 编译。在内联汇编块中使用的 Solidity 代码或 Yul 不会触发它。 - -阅读我们的[博客文章描述该 bug](https://blog.soliditylang.org/2023/11/08/verbatim-invalid-deduplication-bug/ "https://blog.soliditylang.org/2023/11/08/verbatim-invalid-deduplication-bug/"),了解它是如何表现的,可能受到影响的合约类型以及其他技术细节。 - -## 更新日志 - -### 编译器特性 - -* 命令行界面:现在始终可以提供一个空的 `--yul-optimizations` 序列。 -* 标准 JSON 接口:现在始终可以提供一个空的 `optimizerSteps` 序列。 - -## 如何安装/升级 - -要升级到 Solidity 编译器的最新版本,请按照我们文档中提供的[安装说明](https://docs.soliditylang.org/en/v0.8.23/installing-solidity.html "https://docs.soliditylang.org/en/v0.8.23/installing-solidity.html")。 - -你可以在这里下载 Solidity 的新版本:[v0.8.23](https://github.com/ethereum/solidity/releases/tag/v0.8.23 "https://github.com/ethereum/solidity/releases/tag/v0.8.23")。如果你想从源代码构建,请不要使用 GitHub 自动生成的源代码存档。而是使用 [`solidity_0.8.23.tar.gz`](https://github.com/ethereum/solidity/releases/download/v0.8.23/solidity\_0.8.23.tar.gz "https://github.com/ethereum/solidity/releases/download/v0.8.23/solidity\_0.8.23.tar.gz"),并查看[我们的源代码构建文档](https://docs.soliditylang.org/en/v0.8.23/installing-solidity.html#building-from-source "https://docs.soliditylang.org/en/v0.8.23/installing-solidity.html#building-from-source")。 - -我们建议所有 Solidity 开发者始终升级到 Solidity 的最新版本,以便利用改进、优化,最重要的是 bug 修复。 - -最后,我们要感谢所有帮助实现这个发布的贡献者! \ No newline at end of file diff --git a/_posts/2023-11-15-exit-games-for-EVM-validiums-the-return-of-Plasma.md b/_posts/2023-11-15-exit-games-for-EVM-validiums-the-return-of-Plasma.md deleted file mode 100644 index fe4cff0..0000000 --- a/_posts/2023-11-15-exit-games-for-EVM-validiums-the-return-of-Plasma.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -layout: post -title: "【译】退出 EVM 有效性验证:Plasma 的回归" -date: 2023-11-15 16:00:00 +0800 -categories: vitalik -permalink: /vitalik/neoplasma -tags: vitalik l2 Plasma ---- - -> 原文链接:[https://vitalik.ca/general/2023/11/14/neoplasma.html](https://vitalik.ca/general/2023/11/14/neoplasma.html) - -*特别感谢 Karl Floersch、Georgios Konstantopoulos 和 Martin Koppelmann 的反馈、审查和讨论。* - -[Plasma](https://ethereum.org/en/developers/docs/scaling/plasma/) 是一类区块链扩展解决方案,允许除存款、提款和默克尔根之外的所有数据和计算都保存在链下。这为我们带来了非常大的可扩展性收益,而这些收益并不受链上数据可用性的瓶颈限制。Plasma 最早是在[2017 年发明的](http://plasma.io/plasma-deprecated.pdf),并在 2018 年看到了许多迭代版本,其中最值得注意的是 [Minimal Viable Plasma](https://ethresear.ch/t/minimal-viable-plasma/426)、[Plasma Cash](https://ethresear.ch/t/plasma-cash-plasma-with-much-less-per-user-data-checking/1298)、[Plasma Cashflow](https://hackmd.io/DgzmJIRjSzCYvl4lUjZXNQ?view#%F0%9F%9A%AA-Exit) 和 [Plasma Prime](https://ethresear.ch/t/plasma-prime-design-proposal/4222)。不幸的是,由于(i)大量客户端数据存储成本和(ii)Plasma 的基本限制使其[难以推广到除支付以外的其他领域](https://medium.com/@kelvinfichter/why-is-evm-on-plasma-hard-bf2d99c48df7),Plasma 在很大程度上已经被 [Rollup](https://vitalik.ca/general/2021/01/05/rollup.html) 所取代。 - -有效性证明的出现(又称 [ZK-SNARKs](https://vitalik.ca/general/2021/01/26/snarks.html)) 给我们提供了重新考虑这一决定的理由。Plasma 在支付方面的主要挑战,即客户端数据存储,可以通过有效性证明有效地解决。此外,有效性证明提供了一系列工具,使我们能够构建一个运行 EVM 的类似 Plasma 的链。然而,Plasma 的安全性保证并不会覆盖所有用户,因为使 Plasma 式退出游戏无法推广到许多复杂应用程序的根本原因仍然存在。然而,在实践中,资产的非常大比例仍然可以得到安全保障。 - -本文描述了如何将 Plasma 的思想扩展到这样的情况。 - -## 概述:Plasma 的工作原理 - -最容易理解的 Plasma 版本是 Plasma Cash。Plasma Cash 通过将每个单独的币视为一个独立的 NFT,并跟踪每个币的独立历史来运作。Plasma 链有一个*操作者*,负责创建并定期发布区块。每个区块中的交易都存储在一个稀疏的默克尔树中:如果一个交易将币 `k` 的所有权转移,它将出现在树的位置 `k`。当 Plasma 链操作者创建一个新区块时,他们会将默克尔树的根发布到链上,并直接向每个用户发送对应于他们拥有的币的默克尔分支。 - -![](https://img.learnblockchain.cn/attachments/migrate/1700007996615) - -*假设这是 Plasma Cash 链中的最后三个交易树。然后,假设所有先前的树都是有效的,我们知道 Eve 目前拥有币 1,David 拥有币 4,George 拥有币 6。* - -任何 Plasma 系统中的主要风险都在于操作者的不当行为。这可以通过两种方式发生: - -1. 发布一个**无效区块**(例如,操作者包括一笔交易,即使 Fred 在那时并不拥有币 1,也将币 1 从 Fred 发送给 Hermione) -2. 发布一个**不可用区块**(例如,操作者未向 Bob 发送他的其中一个区块的默克尔分支,导致他无法向其他人证明他的币仍然有效且未花费) - -如果操作者以与用户资产相关的方式不当行事,用户有责任立即退出(特别是在 7 天内)。当用户(“**退出者**”)退出时,他们提供一个默克尔分支,证明了将该币从**先前所有者**转移到他们的交易。这启动了一个为期 7 天的**挑战期**,在此期间,其他人可以通过提供以下三种情况之一的默克尔证明来挑战该退出: - -1. **非最新所有者**:退出者签署的后续交易,将退出者的币转移到其他人 -2. **双重花费**:将币从先前所有者转移到其他人的交易,该交易在将币转移到退出者的交易之前已被包含 -3. **无效历史**:在此之前(在过去的 7 天内)转移币的交易没有相应的支出。退出者可以通过提供相应的支出来回应;如果他们没有这样做,退出失败。 - -![](https://img.learnblockchain.cn/attachments/migrate/1700007998029) - -根据这些规则,任何拥有币 `k` 的人都需要查看过去一周内所有历史树中位置 `k` 的所有默克尔分支,以确保他们实际拥有币 `k` 并且可以退出。他们需要存储包含资产转移的所有分支,以便他们可以回应挑战并安全地退出他们的币。 - -### 推广到可替代代币 - -上述设计适用于 NFT。然而,比 NFT 更常见的是可替代代币,如 ETH 和 USDC。将 Plasma Cash 应用于可替代代币的一种方法是简单地将每个币的小面额(例如 0.01 ETH)视为一个独立的 NFT。不幸的是,如果这样做,退出的 gas 成本将会太高。 - -一种解决方案是通过将许多相邻的币视为单个单位来进行优化,这些币可以一次性转移或退出。有两种方法可以做到这一点: - -1. 几乎原样使用 Plasma Cash,但使用复杂的算法快速计算非常多对象的默克尔树,如果许多相邻对象相同,这其实并不难;你可以在[这里](https://github.com/ethereum/research/blob/master/proof_of_solvency/crazy_merkle_tree.py)看到一个 Python 实现。 -2. 使用 [Plasma Cashflow](https://hackmd.io/DgzmJIRjSzCYvl4lUjZXNQ?view#%F0%9F%9A%AA-Exit),它简单地将许多相邻的币表示为单个对象。 - -然而,这两种方法都会遇到*碎片化*的问题:如果你从数百人那里每人收到 0.001 ETH,他们购买咖啡,你将在树中的许多位置拥有 0.001 ETH,因此实际退出这些 ETH 仍然需要提交许多独立的退出,使得 gas 费用高得令人望而却步。已经开发了碎片化协议,但实施起来很棘手。 - -或者,我们可以重新设计系统,以考虑更传统的["未花费交易输出"(UTXO)模型](https://www.ledger.com/academy/glossary/unspent-transaction-output-utxo)。当你退出一个币时,你需要提供这些币的最近一周的历史,任何人都可以通过证明这些历史币已经退出来挑战你的退出。 - -![](https://img.learnblockchain.cn/attachments/migrate/1700007999257) - -*右下角的 0.2 ETH UTXO 的提取可以通过展示其历史中的任何 UTXO 的提取来取消,如绿色所示。特别要注意的是,左中和左下的 UTXO 是祖先,但左上的 UTXO 不是。这种方法类似于 2013 年左右的染色币协议中的[基于顺序的着色](https://github.com/killerstorm/colored-coin-tools/blob/master/colors.md)思想。* - -有各种各样的技术可以做到这一点。在所有情况下,目标都是跟踪不同历史时刻下的“相同币”的概念,以防止“相同币”被提取两次。 - -### 推广到 EVM 的挑战 - -不幸的是,将 Plasma 推广到 EVM 之外要困难得多。一个关键挑战是 EVM 中许多状态对象并没有明确的“所有者”。Plasma 的安全性取决于每个对象都有一个所有者,该所有者有责任监视并确保链的数据可用,并在任何问题发生时退出该对象。然而,许多以太坊应用程序并不是这样工作的。例如,Uniswap 流动性池并没有单一所有者。 - -另一个挑战是 EVM 并不试图限制依赖关系。在第 N 块中,帐户 A 中持有的 ETH 可能来自第 N-1 块的任何地方。为了退出一致的状态,EVM Plasma 链需要一个退出游戏,其中,在极端情况下,希望使用第 N 块的信息退出的人可能需要支付费用将整个第 N 块状态发布到链上:这将是数百万美元的 gas 成本。基于 UTXO 的 Plasma 方案没有这个问题:每个用户都可以从他们拥有数据的最新块退出他们的资产。 - -第三个挑战是 EVM 中无限的依赖关系使得更难以对证明*有效性*具有对齐的激励。任何状态的有效性取决于其他所有事物,因此证明任何一件事都需要证明所有事物。在这种情况下解决失败通常无法做到激励兼容,因为[数据可用性问题](https://github.com/ethereum/research/wiki/A-note-on-data-availability-and-erasure-coding)。一个特别令人讨厌的问题是,我们失去了在基于 UTXO 的系统中存在的保证,即对象的状态不能在没有所有者同意的情况下发生变化。这个保证非常有用,因为它意味着所有者始终知道他们资产的最新可证明状态,并简化了退出游戏。没有它,创建退出游戏变得更加困难。 - -## 有效性证明如何缓解许多这些问题 - -有效性证明对改进 Plasma 链设计的最基本作用是证明每个 Plasma 区块的有效性。这*极大地*简化了设计空间:这意味着我们唯一需要担心的是操作者的攻击是*不可用*区块,而不是*无效*区块。例如,在 Plasma Cash 中,它消除了对历史挑战的需要。这将用户需要下载的状态从过去一周内每个区块的一个分支减少到每个资产的一个分支。 - -此外,在最近状态的提取(在操作者诚实的常见情况下,所有提取都将来自最近状态)不受非最新所有者挑战的限制,因此在经过有效性证明的 Plasma 链中,这样的提取根本不会受到任何挑战。这意味着,**在正常情况下,提取可以是瞬时的!** - -### 扩展到 EVM:并行 UTXO 图 - -在 EVM 情况下,有效性证明还可以让我们做一些聪明的事情:它们可以用于实现 ETH 和 ERC20 代币的并行 UTXO 图,并*通过 SNARK 证明 UTXO 图和 EVM 状态之间的等价性*。一旦你有了这个,你可以在 UTXO 图上实现一个“常规”的 Plasma 系统。 - -![](https://img.learnblockchain.cn/attachments/migrate/1700008000284) - -这让我们规避了 EVM 的许多复杂性。例如,在基于账户的系统中,某人可以在没有你的同意的情况下编辑你的账户(通过发送币并因此增加其余额),这并不重要,因为 Plasma 构造不是在 EVM 状态本身上,而是在与 EVM 平行的 UTXO 状态上,你收到的任何币都将是独立的对象。 - -### 扩展到 EVM:总状态退出 - -已经有人提出了更简单的方案来创建一个“Plasma EVM”,例如 [Plasma Free](https://ethresear.ch/t/plasma-free/16570),以及在此之前的[2019 年的这篇文章](https://ethresear.ch/t/minimal-fully-generalized-s-ark-based-plasma/5580)。在这些方案中,任何人都可以在 L1 上发送消息,强制操作者要么包括一个交易,要么提供状态的特定分支。如果操作者未能这样做,链将开始回滚区块。一旦有人发布了整个状态的完整副本,或者至少所有用户标记为潜在丢失的所有数据,链就会*停止*回滚。进行提取可能需要发布悬赏,这将支付该用户发布如此大量数据的 gas 成本的份额。 - -这样的方案的弱点在于它们不允许在正常情况下进行瞬时提取,因为总是有可能链需要回滚最新状态。 - -## EVM Plasma 方案的局限性 - -**这样的方案非常强大,但无法为所有用户提供完全的安全保障**。它们最明显崩溃的情况是某个特定状态对象没有明确的经济“所有者”的情况。 - -考虑一个 CDP(抵押债务头寸)的情况,这是一个智能合约,用户的代币被锁定,只有在用户偿还债务后才能释放。假设用户在一个 CDP 中锁定了 1 ETH(本文撰写时约为 2000 美元),并欠下 1000 DAI 的债务。现在,Plasma 链停止发布区块,用户拒绝退出。用户可以选择永远不退出。此时,用户拥有一个“自由选择”:如果 ETH 的价格跌破 1000 美元,他们可以放弃 CDP,如果 ETH 的价格保持在 1000 美元以上,最终他们可以退出。一般来说,这样的恶意用户可以从中获利。 - -另一个例子是隐私系统,例如 Tornado Cash 或 [Privacy Pools](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4563364)。考虑一个有五个存款人的隐私系统: - -![](https://img.learnblockchain.cn/attachments/migrate/1700008001510) - -*隐私系统中的 ZK-SNARKs 隐藏了进入系统的代币所有者和离开系统的代币所有者之间的联系。* - -假设只有橙色已经提取了资金,此时 Plasma 链操作员停止发布数据。假设我们使用 UTXO 图方法,并采用先进先出规则,因此每个代币都与其下方的代币匹配。然后,橙色可以提取他们的预混合和后混合代币,系统会将其视为两个独立的代币。如果蓝色尝试提取他们的预混合代币,橙色的更新状态将会覆盖它;与此同时,蓝色将无法提取他们的后混合代币。 - -如果允许其他四个存款人提取隐私合约本身(这将覆盖存款),然后在 L1 上取出代币,这个问题是可以解决的。然而,实际实现这样的机制需要隐私系统开发人员额外的努力。 - -还有其他解决隐私问题的方法,例如 [Intmax](https://eprint.iacr.org/2023/1082.pdf) 方法,它涉及在链上以 Rollup 方式放置几个字节,以及一个类似 Plasma 的操作员在个体用户之间传递信息。 - -Uniswap LP 头寸也存在类似的问题:如果你在 Uniswap 头寸中用 USDC 交易 ETH,你可以尝试提取交易前的 USDC 和交易后的 ETH。如果你与 Plasma 链操作员勾结,流动性提供者和其他用户将无法访问交易后的状态,因此他们将无法提取他们的交易后的 USDC。需要特殊逻辑来防止这种情况发生。 - -## 结论 - -2023 年,Plasma 是一个被低估的设计空间。Rollup 仍然是金标准,并且具有无法匹敌的安全性能。特别是从开发者体验的角度来看:没有任何东西可以与应用程序开发者无需考虑其应用程序内的所有权图和激励流程的简单性相匹敌。 - -然而,Plasma 让我们完全规避了数据可用性问题,大大降低了交易费用。对于本来可能是 validiums 的链来说,Plasma 可以是一个重大的安全升级。[ZK-EVM 终于在今年成为现实](https://vitalik.ca/general/2022/08/04/zkevm.html),这使得重新探索这一设计空间成为一个绝佳的机会,并提出更加有效的构建来简化开发者体验并保护用户资金。 \ No newline at end of file diff --git a/_posts/2024-09-13-intro-to-safe-wallet.md b/_posts/2024-09-13-intro-to-safe-wallet.md new file mode 100644 index 0000000..b30acb3 --- /dev/null +++ b/_posts/2024-09-13-intro-to-safe-wallet.md @@ -0,0 +1,104 @@ +--- +layout: post +title: 'Safe 多签钱包简介' +date: '2024-09-13' +category: wallet +permalink: /wallet/guide-to-safe-wallet +tags: wallet safe gnosis-safe +--- + +## 引言 + +在区块链世界中,安全始终是一个关键话题。随着加密资产价值的增长,如何安全管理这些资产变得越来越重要。多签钱包作为一种增强资产安全的有效工具,逐渐受到市场的广泛认可。[Safe 多签钱包](https://safe.global/wallet)(前身为 Gnosis Safe)便是其中的佼佼者,它不仅适用于以太坊和其他 EVM 兼容的区块链,还支持执行复杂的交易操作。 + +多签钱包的核心理念是将私钥的控制权分散到多个参与者手中,通过要求多个签名来授权交易,从而提高资金的安全性。Safe 在这一基础上,通过以太坊的智能合约技术,提供了一个安全可靠且功能丰富的多签钱包解决方案,这种解决方案不仅保障了资金的安全,还提供了灵活的操作性,满足了不同用户的多样化需求。 + + +## 多重签名交易流程 + +Safe 多签钱包采用了一种称为“n-of-m”(其中 n 是 m 的子集)的多重签名机制。该机制要求在 m 个钱包所有者中有至少 n 个所有者签名,才能执行交易。 + +多重签名的交易流程如下: + +1. 交易提议:任一所有者均可发起交易提议。 +2. 签名收集:其他所有者对交易进行离线签名。 +3. 交易执行:一旦收集到足够的签名并达到预设的签名阈值,任何人都可以执行该交易。 + +这种机制不仅确保了交易的安全性,还提供了灵活性,允许用户根据自身需求设定不同的签名门槛。 + +例如:设置为 2-of-3 的 Safe 钱包由三个账户共同拥有,需要至少两个账户的签名才能执行交易。 + +假设 Alice、Bob 和 Charlie 是该钱包的共有者。Alice 提出向另一地址转账 1 ETH 的提案,并已完成自己的签名。Bob 或 Charlie 在审查提案无误后,可以添加他的签名。一旦签名达到所需数量,任何人均可执行该交易,但执行该交易的人需要支付 gas 费用。 + + +## Safe 多签钱包的技术架构 + +Safe 多签钱包的核心是一系列智能合约,主要包括(v1.3.0): + +1. **主合约(GnosisSafe)**:负责管理钱包的核心逻辑,包括所有者管理、交易执行等。 +2. **代理合约(GnosisSafeProxy)**:每个 Safe 钱包实例都是一个代理合约,指向主合约的实现。 +3. **模块管理(ModuleManager)**:可插拔的功能扩展,如社交恢复等。 +4. **防护模块(GuardManager)**:用于增加额外的安全检查(制定特定地址才能执行、重入保护、允许/禁止 delegatecall 特定地址等)。 + + +## 功能特色 + +### 直观和友好的用户界面 +用户可以方便地在其[官网](https://app.safe.global/welcome/accounts)上创建多签钱包、查看钱包资产、发起多签交易。 + +![Safe UI](../image/safe_multisig_ui.png) +### 支持多个区块链网络 + +Safe 支持多个主流区块链网络,包括但不限于: + +- Ethereum +- Polygon +- BNB Chain +- Gnosis Chain +- Arbitrum +- Optimism + +这种多链支持使得用户可以在不同网络间管理资产,提高了资产管理的灵活性。 + +### 资产管理能力 + +Safe 不仅支持原生代币(如 ETH),还支持各种 ERC20 代币和 ERC721 NFT。用户可以在 Safe 钱包界面直接查看和管理这些资产。 + +### 与 DeFi 协议的集成 + +Safe 提供了与多个 DeFi 协议的原生集成,例如: + +- Uniswap:直接在 Safe 界面进行代币交换。 +- Aave:管理借贷头寸。 + +这些集成大大提高了资金使用的效率和便利性。 + +## 使用场景 + +### 企业资金管理 + +对于企业来说,Safe 提供了一个理想的资金管理解决方案: + +- 多人授权:防止单点故障和内部欺诈。 +- 交易透明:所有交易都可以在链上追踪和审计。 +- 灵活控制:可以为不同级别的支出设置不同的审批流程。 + +### DAO 治理 + +在去中心化自治组织(DAO)中,Safe 可以作为国库管理工具: + +- 提案执行:通过多签机制执行已通过的提案。 +- 资金分配:根据社区决策管理和分配资金。 +- 权限管理:可以为不同的工作组设置不同的权限。 + +### 个人资产保护 + +对于持有大量加密资产的个人,Safe 提供了额外的安全层: + +- 防止私钥丢失:即使丢失一个私钥,也不会影响资金安全。 +- 继承计划:可以设置时间锁和继承人,确保资产可以被合法继承。 +- 分散风险:可以将资产分散到多个设备或地理位置。 + + +## 结语 +区块链世界中,资产安全是用户首要关注的问题。Safe 多签钱包依托智能合约和多签机制,提供安全和灵活的资产管理方案,支持多个主流区块链网络并与多种 DeFi 协议集成。尽管 Safe 具有诸多优点,用户应基于自己的需求和安全考量,仔细评估选择合适的多签钱包解决方案。 \ No newline at end of file diff --git a/_posts/2024-09-14-safe-wallet-sdk.md b/_posts/2024-09-14-safe-wallet-sdk.md new file mode 100644 index 0000000..af383d0 --- /dev/null +++ b/_posts/2024-09-14-safe-wallet-sdk.md @@ -0,0 +1,155 @@ +--- +layout: post +title: '使用 Safe SDK 创建多签钱包' +date: '2024-09-14' +category: wallet +permalink: /wallet/safe-wallet-sdk +tags: wallet safe gnosis-safe +--- + +## Safe 简介 + +[Safe](https://app.safe.global/)(前身为 Gnosis Safe)是一个建立在以太坊网络上的智能合约钱包平台,专注于提供安全、灵活的数字资产管理解决方案。 +Safe 广泛应用于个人资产管理、DAO 治理、企业财务等领域,是目前以太坊生态中最受信赖的多签钱包解决方案之一。 + +更多关于 Safe 多签钱包的介绍见[这里](./2024-09-13-intro-to-safe-wallet.md)。 + +现在,让我们深入了解如何使用 Safe SDK 来创建和使用多签钱包。 + +## 使用教程 + +Safe 提供了功能强大的 JavaScript SDK,让开发者能够轻松集成和使用 Safe 的功能。 + +以下是使用 JavaScript SDK 与钱包交互的详细示例代码: + +### 准备工作 + +首先,确保你的开发环境中已安装 Node.js 和 npm/yarn。然后,安装必要的依赖: + +```bash +npm install ethers @safe-global/protocol-kit @safe-global/api-kit +# 或者 +yarn add ethers @safe-global/protocol-kit @safe-global/api-kit +``` + +### 部署一个多签钱包 +```javascript +const { SafeFactory } = require('@safe-global/protocol-kit'); + +const safeFactory = await SafeFactory.init({ + provider: '', // 替换为实际的 RPC url + signer: '', // 私钥,用于部署多签钱包合约,该私钥对应的账户不一定是多签的签名者 +}) + +// 创建一个 2-3 多签钱包 +const safeAccountConfig = { + owners: [ + '', // 替换签名者地址 + '', // 替换签名者地址 + '', // 替换签名者地址 + ], + threshold: 2, +} + +// 部署多签钱包合约 +const safeWallet = await safeFactory.deploySafe({ safeAccountConfig }); + +const safeAddress = await safeWallet.getAddress(); + +console.log('Safe 钱包已部署'); +console.log(`https://app.safe.global/sep:${safeAddress}`); // 以 sepolia 为例 +``` + +### 读取多签钱包相关信息 +```javascript + const Safe = require('@safe-global/protocol-kit').default; + + const safeWallet = await Safe.init({ + provider: '', // 替换为实际的 RPC url + safeAddress: '', //替换为实际的多签钱包地址 + }) + + const threshold = await safeWallet.getThreshold(); + const owners = await safeWallet.getOwners(); + + console.log(`Threshold: ${threshold}`); // 阈值(最小签名数量) + console.log(`Owners: ${owners.join(', ')}`); // 签名者 +``` + +### 发起一笔 ETH 转账多签交易 + +(记得先给该多签钱包发送足够的 ETH) +```javascript +const signerPrivateKey = "0x..."; // 替换为多签钱包签名者之一的私钥 + +// 转账 0.01 ether +const safeTransactionData = { + to: '', // 替换为收款人地址 + data: '0x', + value: ethers.parseUnits('0.01', 'ether').toString() +} + +// 创建多签交易 +const safeTransaction = await safeWallet.createTransaction({ 'transactions': [safeTransactionData] }); + +// 获取这笔交易的哈希值 +const safeTxHash = await safeWallet.getTransactionHash(safeTransaction); + +// 对交易哈希值进行签名 +const senderSignature = await safeWallet.signHash(safeTxHash); + +const apiKit = new SafeApiKit({ + chainId: await safeWallet.getChainId(), +}) + +// 提交交易到 Safe 服务(这样,其他签名者可以在 Safe 网站中看到待签名的交易) +await apiKit.proposeTransaction({ + 'safeAddress': '', // 替换为实际的多签钱包地址 + 'safeTransactionData': safeTransaction.data, + 'safeTxHash': safeTxHash, + 'senderAddress': new Wallet(signerPrivateKey).address, + 'senderSignature': senderSignature.data, +}) +``` + +### 确认交易 + +```javascript +// ... (省略前面的代码) +const transaction = (await apiKit.getPendingTransactions(safeAddress)).results[0]; + +if (!transaction) return; + +const safeTxHash = transaction.safeTxHash; + +const signature = await safeWallet.signHash(safeTxHash); +const response = await apiKit.confirmTransaction(safeTxHash, signature.data); +console.log('已确认交易:\n', response); +``` + +### 执行交易 + +```javascript +// ... (省略前面的代码) +if (transaction.confirmations.length !== transaction.confirmationsRequired) { + console.log('未达到签名确认数'); + return; +} + +const safeTransaction = await apiKit.getTransaction(transaction.safeTxHash); +const executeTxResponse = await safeWallet.executeTransaction(safeTransaction); +const receipt = await executeTxResponse.transactionResponse?.wait(); + +console.log('交易已执行,hash:', receipt.hash); +``` + +以上完整代码示例见:[demos](https://github.com/0xdwong/web3-practice/blob/main/src/gnosis_safe/README.md) + + +## 结语 +本文详细介绍了使用 Safe JavaScript SDK 创建多签钱包和执行交易等基本步骤。随着对 Safe 的深入探索,其高级特性如模块化扩展、批量交易等将为你开启更广阔的应用空间,助你从容应对各种复杂的资产管理需求。 + +如果你想进一步学习和探索 Safe 的功能,可以参考以下资源: + +- [Safe官方文档](https://docs.safe.global/) +- [Safe{Core} SDK](https://docs.safe.global/sdk/overview) \ No newline at end of file diff --git a/image/erc6551.png b/image/erc6551.png deleted file mode 100644 index 38d746d..0000000 Binary files a/image/erc6551.png and /dev/null differ diff --git a/image/safe_multisig_ui.png b/image/safe_multisig_ui.png new file mode 100644 index 0000000..a8aaf1b Binary files /dev/null and b/image/safe_multisig_ui.png differ