政府说我们没有陷入衰退,但与此同时,我们听说几乎每个经济部门都在飞涨的通货膨胀、利率上升和裁员。 尽管加密货币和 TradFi 受到的影响最为严重,但许多公司仍在构建其代币、协议和 DeFi 产品。你是其中之一吗? 今天,我将谈论数据类型,等等。我有非常重要的话要说。你可能会把我想象成麻省理工学院一位 60 多岁的教授,用关于不再重要的科目的讲座来折磨学生。但事实并非如此。 数据类型仍然很重要,忽视它们会导致严重的后果。我将尝试简要介绍所有潜在问题并解决它们,这样您就不会发现您花在阅读本文上的 8 分钟是浪费了。 JavaScript 和 Python 等现代语言正在使用“鸭子类型”来确定变量的类型。如果我们将这种公式 分配给一个变量,语言解释器就会知道它正在处理数字,并且会对这个文字执行数学运算。 a = 2 + 2 鸭子类型可以用这句话来解释:“如果它走路像鸭子,叫起来像鸭子,那么它一定是鸭子”。当您仔细研究它的含义时 - 它非常有道理。如果 literal 有字母和数字,那肯定是字符串,这就明白了。但如果它有数字呢? 它是 、 、 、 还是 。在大多数情况下,您的语言解释器将以正确的方式处理类型。当您的程序需要对大数运行(甚至是简单的)方程式时,问题就出现了。 boolean integer decimal float date “如果它走路像鸭子,叫起来像鸭子,那么它一定是鸭子”,对吧?其实不然。 以太坊面额简而言之 在接下来的段落中,我指的是以太坊的通用名称 - 和 。让我向您简要介绍一下他们,以便我们讲共同语言。 wei gwei 最小面额是 , 等于 (18 个零) 我重复一遍——18 个零。很难将我们的思想集中在如此庞大的数字上,但它们会产生影响并且非常重要。 1 wei 1 以太 1,000,000,000,000,000,000 Wei 。 下一个通用面额是 。 等于 (9 个零) Gwei 更适合人类——说到底,每个人都想成为百万富翁,对吧? (眨眼眨眼) 1 gwei 1 以太 1,000,000,000 gwei 。 让我们总结一下——1 以太等于: 1,000,000,000 gwei(9 个零) 1,000,000,000,000,000,000 wei(18 个零) 技术说明:以太坊有两层——执行层和共识层。执行层使用 wei 来表示以太值,共识层使用 gwei。如果您是区块链开发人员,则需要学习与两者交互。 真实世界的例子:Stakefish 的小费和 MEV 池 我是 的一名软件工程师。我负责构建我们的 DeFi 产品选项板,最近的选项之一是我们的以太坊小费和 MEV 池。 stakefish 从 2022 年 9 月 15 日开始,所有验证者都有资格获得交易提示,并可以参与 MEV 以获得额外奖励。交易提示和 MEV 是在验证者提出新区块时获得的。 我们决定构建一个智能合约,将所有奖励收集到一个公共金库中,并允许用户从中领取他们的份额。我不是要为我们的产品做广告,但我需要设置本文的上下文。 如果您对此产品更感兴趣,可以 。除了我的经验,我不会卖给你任何东西。 在此处阅读更多内容 正如我提到的,我们有一个智能合约可以接收交易提示和验证者获得的 MEV 奖励。这意味着我们的智能合约有相当大的余额。现在有 963+ 个以太币(110 万美元),我们有 8671 个验证者参与其中。 负责以太坊执行和共识层之间同步的关键部分是 。这是一个非常重要的系统,它使我们能够确定哪些验证者正在为矿池做出贡献。 Oracle oracle 是用 Python 写的,但它可以用 JavaScript 写——问题不变,我很快就会证明它。 让我们深入研究代码! 为什么数据类型很重要 智能合约的余额现在等于 963,135,554,442,603,402,422 wei(963 以太币)。这个数字不仅人类难以理解,而且计算机(准确地说是语言解释器)也难以理解。让我们检查一下 JavaScript: const vault_balance = parseInt("963135554442603402422") console.log(vault_balance) // 963135554442603400000 (lost 2422 wei in total) 我只是将余额从 转换为 ,我已经少了 。我们还没有运行任何方程式。 string int 2422 wei 由于许多验证者的贡献,智能合约余额如此之高。现在,让我们计算一下合约余额中验证者的平均份额: const vault_balance = parseInt("963135554442603402422") const validator_count = 8671 const avg_validator_contribution = vault_balance / validator_count // 111075487768723730 (lost 7 wei per validator) 平均份额为 0.111 以太币。但这个数额不正确——我们实际上少了 7 wei。总共是 (7 wei 乘以 8671 个验证者)。我稍后会显示正确的数字。 60,697 wei 进一步深入损失的兔子洞 - 让我们计算每个给定验证器的奖励总量。请记住,用户需要存入 32 个以太币才能启动验证器,因此我将从验证器余额中扣除。 我将以一个为智能合约做出贡献的随机验证者为例,该验证者的余额为 32.779 以太币。 const vault_balance = parseInt("963135554442603402422") // (lost 2422 wei) const validator_count = 8671 const avg_validator_contribution = vault_balance / validator_count // (lost 7 wei) const initial_deposit = parseInt("32000000000000000000") const validator_balance = parseInt("32779333896000000000") const total_validator_rewards = validator_balance - initial_deposit + avg_validator_contribution // 890409383768723700 (lost 23 wei per validator) 该验证者获得的总奖励等于 0.8904 以太币,但这个值也不准确。此时,我们总共 (23 wei 乘以 8671 验证者)。如您所见,这种计算数字的方法是不可持续的。 199,443 wei 出了什么问题? 上面的代码有两个问题: 在 JavaScript 中,整数的最大安全值仅等于 。这意味着它最多可以处理 (0.009 以太币) 2^53 - 1 9007199254740991 wei 从技术上讲,我们可以使用 ,但我们会遇到除法问题。我们最终会得到“浮动”值。浮点数是金融界万恶之源,因为它们是近似值。这意味着他们失去了精度。我们需要使用小数。 (decimal 和 float 之间的主要区别在于 decimal 存储精确值而 float 近似值。) BigInt 如果您曾经用 JavaScript 编写过任何与以太坊相关的代码,那么您一定听说过 。该库包含与区块链交互所需的所有实用程序。为了解决上述问题,我们将使用一种名为 的工具,它支持极大的数字并以正确的方式处理小数。 ethers.js BigNumber 我们开始做吧! const vault_balance = BigNumber.from("963135554442603402422") // no loss const validator_count = BigNumber.from(8671) const avg_validator_contribution = vault_balance.div(validator_count) // no loss // 111075487768723723 const initial_deposit = BigNumber.from("32000000000000000000") const validator_balance = BigNumber.from("32779333896000000000") const total_validator_rewards = validator_balance.sub(initial_deposit).add(avg_validator_contribution) // 890409383768723723 如您所见,现在我们得到了准确的数字。我怎么知道这实际上是正确的数字?我将在 Python 中重复同样的练习来证明我是对的。 让我们在 Python 中尝试一下 Python 支持长整数,因此值不会像我们在 JavaScript 中看到的那样突然被截断。不幸的是,它仍然默认将所有浮点数确定为 : float vault_balance = int("963135554442603402422") # no loss validator_count = 8671 avg_validator_contribution = vault_balance / validator_count # 111075487768723728 (5 wei too much) initial_deposit = int("32000000000000000000") validator_balance = int("32779333896000000000") total_validator_rewards = validator_balance - initial_deposit + avg_validator_contribution # 890409383768723712 (lost 11 wei) 想知道它到底在哪里失去了精度?该部门将 为 而不是 。正确的片段应该是这样的: avg_validator_contribution float decimal vault_balance = Decimal("963135554442603402422") validator_count = Decimal(8671) avg_validator_contribution = vault_balance / validator_count # 111075487768723723 initial_deposit = Decimal("32000000000000000000") validator_balance = Decimal("32779333896000000000") total_validator_rewards = validator_balance - initial_deposit + avg_validator_contribution # 890409383768723723 现在,Python 和 JavaScript 返回的值是准确的。自己检查! 这些损失是微不足道的,很容易被遗漏。通常,当它们随着时间的推移复合并增长到可观的数量时,我们就会发现它们。 像这样的情况总是让开发人员头疼,也让财务或法律等其他部门头疼。您应该始终测试您的公式,并且永远不要使用漂亮的整数来这样做! 如果您 这对我来说意义非凡。我的活动重点是软件工程和区块链。我正在开源我的大部分工作,所以你可能想查看 。 在 Twitter 上关注我, 我的 GitHub