Skip to main content

提供者、签署者、ABI 和批准流

有几个主题我们想提供一些信息,但不够详细,不值得有自己的层次。这个层次的目的是把这样的一堆独立的主题组合在一起,并介绍一些有助于牢记的东西。

目录

  • 提供者和签名者
  • BigNumbers
  • ABI
  • ERC20 审批流程

提供者和签署者

在构建智能合约的接口时,你通常会遇到这两个术语--提供者和签名者。虽然当你真正开始使用它们时,你会对它们有一个更好的理解,但我们将尝试对它们的含义进行简要的解释。

我们知道,要向区块链读取或写入数据,我们需要通过一个以太坊节点进行通信。该节点包含区块链状态,允许我们读取数据。它也可以向矿工广播交易,让我们写数据。

请注意,在你想向区块链写入数据的情况下,节点只需要广播交易。因为如果你只是在读取已经存在的数据,矿工不需要做任何事情,他们已经完成了他们的工作。

提供者是一个以太坊节点连接,允许你从其状态中读取数据。你将使用提供者来做一些事情,比如在智能合约中调用只读函数,获取账户余额,获取交易细节等。

签名者是一个以太坊节点连接,允许你向区块链写入数据。你将使用 Signer 来做一些事情,如在智能合约中调用写函数,在账户之间转移 ETH 等。要做到这一点,签名者需要访问一个它可以用来代表账户进行交易的私钥。

此外,签约人可以做提供者的一切。你可以使用签名者做这两件事,即读和写,但提供者只适合读数据。

像 Metamask 这样的钱包,默认情况下,在你的浏览器中注入一个提供者。因此,dApps 可以使用你的 Metamask Provider 从你的钱包所连接的区块链网络中读取数值。

然而,有时候,你希望用户进行交易,而不仅仅是读取数据。当然,Metamask 不能随便与随机网站分享你的私钥--那会很疯狂。对于这一点,Metamask 也允许网站请求签名者。因此,当一个 DApp 试图向区块链发送交易时,Metamask 窗口会弹出,要求用户确认该行动。

BigNumbers

在学习 Solidity 时,我们经常阅读和使用 uint256。uint256 的范围从 0 到(2^256) - 1。因此,uint256 数据类型所能容纳的最大数字是天文数字。

具体来说,uint256 的最大值是。

115792089237316195423570985008687907853269984665640564039457584007913129639935

作为比较,一百万是

1000000

很明显,uint256 可以容纳大得惊人的数字。但这带来了一个问题。

我们通常用 Javascript 构建智能合约的接口。Javascript 的数字数据类型的上限要小得多。

具体来说,Javascript 能容纳的数字类型的最大值只有。

9007199254740991

这与 uint256 所能容纳的数值相差甚远。

所以,假设我们用 Javascript 在智能合约上调用一个函数,返回一个 uint256。如果这个数字大于 Javascript 的最大数字值,这绝对是可能的,那么会发生什么呢?

嗯,事实证明,Javascript 不能支持这一点。因此,我们必须使用一种叫做 BigNumber 的特殊类型。用于与以太坊节点互动的库--ethers.js 和 web3.js--都支持 BigNumbers。

BigNumber 是一个自定义的类库,用 Javascript 编写,它为数学函数引入了自己的函数--add、sub、mul、div 等。BigNumber 对数字的容量明显大于 Javascript 所能支持的。

当我们在下面的层次中写代码时,你会遇到通过调用.add()和.mul()等函数来完成数学运算,而不是我们所知道的典型的+和*运算符--这是因为当我们在处理 BigNumbers 时,需要使用它的数学函数。

至于如果我们试图用 Javascript 数字来做这件事会发生什么,我们会非常容易地溢出或不足。这意味着我们的计算将是完全不正确和不确定的。所以,请牢记这一点。

ABI

ABI 代表应用程序二进制接口。在使用以太坊时,这是比较难理解的事情之一,但我们会尽力解释它。

在新生教程中,以及在教程中你会进一步遇到,你将大量使用 ABI。

当 Solidity 代码被编译时,它被编译成本质上是二进制的字节码。它不包含合约中存在的函数名称、它们包含的参数以及它们返回的值的记录。

但是,如果您想从 Web 应用程序调用 Solidity 函数,则需要一种在合约中调用正确字节码的方法。为此,您需要一种将人类可读的函数名称和参数转换为字节码并返回的方法。

ABI 可以帮助我们实现这一目标。当你编译你的 Solidity 代码时,编译器会自动生成一个 ABI。它包含有关合约中存在的功能的规则和元数据,有助于进行正确的数据来回转换。

所以,当你想调用一个合约时,你需要它的地址(当然),但你也需要提供它的 ABI。像 ethers.js 这样的库使用 ABI 将人类可读的函数编码和解码为字节码,然后在与以太坊节点通信和调用智能合约中的函数时返回。

ERC20 审批流程

过去,我们了解了应付函数,它允许智能合约在调用函数时接受 ETH 付款。如果您想以 ETH 向用户收费以换取某些东西(例如 NFT 销售),这非常有用。

但是,如果你想使用 ETH 以外的东西进行支付怎么办?如果您想使用自己部署的加密货币进行支付怎么办?

这里的事情有点棘手。

由于 ETH 是以太坊的原生货币,而且 ERC20 标准是在以太坊发明之后很久才引入的,因此它们的行为方式并不完全相同。具体来说,接受 ERC20 代币支付并不像在 Solidity 中支付功能那么简单。

payable 关键字仅适用于 ETH 支付。如果您想使用自己的 ERC20 加密货币,那么执行此操作的流程会稍微复杂一些。

首先,让我们稍微考虑一下。

  • 好的,所以你不能像使用 ETH 一样发送 ERC20 代币和函数调用
  • 也许智能合约可以以某种方式从函数调用者的帐户中提取代币?
  • 但这意味着我可以编写一个智能合约,如果有人用我的合约进行交易,它会窃取每个人的代币
  • 所以我们需要一种更安全的方式从某人的账户中提取代币

这是批准和转移流程的用武之地。

ERC20 标准带有 Allowance 的概念。

让我们试着借助一个例子来思考这个问题。

  • 爱丽丝想卖掉她的 NFT
  • Alice 想用她自己的加密货币 AliceCoin 接受她的 NFT 付款
  • Alice 的 NFT 花费 10 AliceCoin
  • 鲍勃拥有爱丽丝币
  • Bob 想购买 Alice 的 NFT
  • Bob 需要一种方法来调用 Alice 的 NFT 智能合约上的函数,该函数将收取 10 Alicecoin 的付款,并将他的 NFT 发送给他
  • 由于智能合约不能直接接受 Alicecoin 作为支付,Alice 在她的 NFT 合约中编写了 ERC20 批准和转账流程

Alicecoin 是一种 ERC20 代币。 ERC20 内置了一些与 Allowance 概念相关的功能。

approve(address spender, uint256 amount)

这允许用户批准不同的地址代表他们花费最多数量的代币。即此功能为花费者提供高达金额的津贴

transferFrom(address from, address to, uint256 amount)

允许用户将金额令牌从一个转移到另一个。

如果调用该函数的用户与发件人地址相同,则从用户余额中删除代币。

如果用户不是发件人地址,则发件人地址必须在过去给予用户许可,以便使用批准功能花费金额代币。


现在继续这个例子:

  • Bob 允许 Alice 的 NFT 合约使用批准功能最多花费 10 个 Alicecoin
  • Bob 调用该函数在 Alice 的 NFT 合约上购买她的 NFT
  • 购买函数内部调用 Alicecoin 上的 transferFrom 并将 10 Alicecoin 从 Bob 的账户转移到 Alice 的账户
  • 由于之前 Bob 允许合约使用最多 10 个 Alicecoin,因此允许此操作
  • 因此,Alice 收到了她的 10 个 Alicecoin,Bob 收到了他的 NFT

好的,那么这对我们意味着什么?

好吧,请注意 Bob 必须如何批准合约,因此合约可以从 Bob 的帐户中提取 Bob 的代币。

因此,如果在 ETH 中接受付款,Bob 基本上必须进行两次交易来复制一次交易中可以完成的行为。

交易 1 - 为合约提供津贴 交易 2 - 调用合约函数,该函数在内部使用津贴将 Bob 的代币转移到不同的地址

因此,如果您正在构建一个 dApp,您需要用户使用 ERC20 代币支付您的智能合约,您还需要让他们同时进行这两项交易。简单地调用你的合约函数,而不首先让你的用户为你的合约提供津贴,将导致函数调用失败。

在大二课程的最后一级构建 DeFi-Exchange 时,我们将遇到此流程的一个用例。由于交易所涉及能够将一种代币转换为另一种代币,因此您需要在交易所智能合约上调用一个函数,该函数接收一个代币并为您提供另一个代币。

要使用您的代币进行交换,交换合约需要获得批准才能从您的账户中提取代币。

因此,如果您想知道为什么在交易所进行交换可以使用两笔交易而不是一笔交易,请记住这一流程。