BTX | Research

Posted on Jul 29, 2022Read on Mirror.xyz

「入门科普 」初涉 Solidity:安全、Gas 优化以及创建众筹平台

原文作者:0xRusowsky 编译:BTX Capital

导读:本文分析了ETH 的三种转移方式:send、transfer、call,三者在交易费优化和安全方面的区别,以及索引和非索引事件的交易费比较。此外,本文还手把手教大家如何创建一个众筹平台。

由于有很多公开的优秀在线资源,因此,我不会解释所有的基本概念,而是尝试解释一些值得分享的有趣事物(即安全性和gas优化技巧),并在尝试解决一些练习时回顾一下我的思考过程。

提示 三种转移方式:send、transfer 和 call 尽管可以使用这 3 种方法来转移以太坊,但它们具有值得了解的关键区别。send()和transfer()曾经被推荐使用,因为它们会将 gas 支出限制在 2300 gwei。利用这个数量的gas,人们可以进行一些次要的 fallback 操作,比如发出事件,甚至更新存储,但这还不足以重入合约。然而,这从来都不是避免重入攻击的好方法,因为 gas 成本会随着协议更新而变化(即伊斯坦布尔分叉增加了SLOD操作码的 gas 成本)。 因此,建议使用低级功能call(),默认转发所有可用的gas。然后,你可以使用 Reentrancy Guard 或 Checks-Effects-Interactions 模式 来保护你的函数。

索引事件参数与非索引(常规)

具有索引参数的事件更容易检索,因为它们在世界状态(在logsBloom中)中被特别索引。因此,它们比只有常规参数的事件稍微贵一些。

这个解释可以通过这个简单的 Foundry 示例来确认:

索引事件和非索引事件之间的 gas 费比较。

注意:日志由主题(最多 4 个)和数据组成。其中一个主题为事件签名保留,因此你最多可以有 3 个索引参数。 在以太坊黄皮书中,可以断言每个主题花费 375 gwei,数据中的每个字节花费 8gwei。

以太坊黄皮书中定义的事件 gas 成本

任务:创建一个基本的众筹平台

搭建一个符合以下要求的简单众筹平台的主干:

  • 任何人都可以创建一个活动(Campaign)来为其项目获取资金。
  • 每个活动至少必须拥有:
    • 名字
    • 所有者
    • 活动类型(初创公司或慈善机构)。
    • 筹资目标
    • 时间限制,不能超过 60 天
  • 上述所有属性都必须在活动创建时定义,并且无法被更新。
  • 在达到给定活动的时间限制之前,任何人都可以通过发送以太坊来资助它。达到时限后,便无法再接收资金。
  • 活动的所有者只有在达到时限后才能提取所筹集的以太坊。
  • 所有者提取资金后,应将活动标记为完全资助 FullyFunded(如果筹得资金 >= 资金目标)或部分资助 PartiallyFunded(如果筹得资金 < 资金目标)。
  • 所有者必须能够取消活动。如果发生这种情况,该活动将无法再收到以太坊。
  • 实施所有相关的事件。

解决方案设计

当有一个想法时,你首先需要弄清楚的是解决方案应该具有的架构设计。在我看来,最合理的做法是为每个活动制定一个独立的合约,这样所有者就可以直接与之互动并拥有完全的所有权。因此,我决定创建一个工厂合约 CampaignFactory 和一个名为 Campaign 的活动合约。

工厂合约的运作模式

鉴于上述的架构,我首先创建了 Campaign 合约,确保它符合所有给定的要求。

你可以在此 GitHub gist中查看完整的脚本https://gist.github.com/0xRusowsky/5b583aeff7e387fa00652a9b079c34ad

首先,我们需要定义变量以及可能需要的任何 枚举 enum 或 结构 structs。

注意 owner 必须是一个 payable address,因为它将具有提取资金的能力。

一旦我们设置了所有变量,我们就可以创建合约构造函数。在这种情况下,构造函数将使用 require() 函数来确保截止日期小于 60 天。

由于在 solidity 中的时间戳是 unix 格式(从1970 年1 月1 日开始以表计算)中表示的,因此构造函数_deadline中使用的输入变量将具有相对范围(require 语句状态_deadline <= 36002460)。尽管如此,全局变量deadline将具有绝对引用,因此我们可以将它与任何给定的时间戳进行比较。

由于 owner 是 a payable address,我们必须更改 _owner 类型以使其符合要求。

在对构建的合约进行编码之后,我创建了相关的事件和一些修改器,这些修改器将有助于我们在活动功能中实现所需的验证。

事件和修饰符的实现。

最后,我们需要实现合约功能。在这种情况下,我们只需要 4 个函数:fallback(), receive(), withdrawFunds(), cancelCampaign().

为了 fallback() 和 receive() 能够从其他合约以及 EOA 中接收以太币,我们需要使它们payable。最重要的是,我们还将添加liveCampaign修改器以确保在活动结束后不再收到捐款。

另一个值得一提的话题是如何处理提款。正如之前在提示部分中所解释的,我们在这里使用低级函数call()。

 执行竞选合约功能。

至此,活动合约完成。现在,我们只需要再创建一个众筹工厂合约。

在这种情况下,工厂合约将只有一个函数,该函数将在调用时部署活动合约的新实例。最重要的是,我们还将添加一些映射以更好地处理已部署的活动。这些映射将按所有者(使用数组)跟踪已部署的活动合约,以及每个人已部署的活动数量(以导航数组)。

执行众筹工厂合约

原文链接:https://journal0xrusowsky.substack.com/p/first-interactions-with-solidity?utm_source=twitter&sd=pf