发布于 2026-01-06 2 阅读
0

JavaScript 开发人员的 Solidity 基础知识(第二部分)

JavaScript 开发人员的 Solidity 基础知识(第二部分)

我的第一篇关于JavaScript 开发人员 Solidity 基础知识的文章获得了如此多的关注,所以我决定写第二篇!

我目前正在学习一本入门级的智能合约开发书籍,现在正在进行主要项目,即一个 DApp 筹款项目。

这本书是用 Truffle、web3.js 和 JavaScript 编写的,为了增加一些趣味性,我用HardhatEthers.js和 TypeScript 替换了这些工具。

以下是我最近的一些发现,这些发现让我有点意外,所以我认为它们可能对新手来说很有趣!

Solidity 事件是为前端设计的。

Solidity 具有事件结构/类型。它允许你为智能合约定义特定的事件,以便在发生你认为有趣的事情时触发。

event MyEvent( uint256 value1, uint256 value2);

function f() public {
  emit MyEvent(123, 456);
}
Enter fullscreen mode Exit fullscreen mode

对谁有用?对你的前端代码来说!

如果我理解正确的话,事件数据会存储在区块链中,但智能合约无法访问这些数据。

事件数据供区块链外部的监听者使用。

您的前端可以为这些事件添加事件监听器,然后,当它开始一个事务时,这些事件将被发出,您就可以在前端执行操作。

smartContract.on("MyEvent", (valueA, valueB) => {
  console.log(valueA, valueB);
})

await smartContract.f();
Enter fullscreen mode Exit fullscreen mode

Ethers.js 使用BigNumber代替BigInt

Solidity 通常需要处理非常大的整数,这超出了NumberJavaScript 类型所能处理的范围。因此,Ethers.js 创建了名为 `int` 的类型BigNumber来解决这个问题。

如今,现代 JavaScript 引擎都有一种BigInt类型可以轻松处理此类值,但情况并非一直如此,Ethers.js 希望保持向后兼容性。

我不知道他们为什么不用BigIntpolyfill,但至少他们提供了一种方法。不过,计算toBigInt()必须用方法!BigNumber

const value1 = ethers.utils.parseEther("1")
const value2 = ethers.utils.parseEther("2")

const result = value1.add(value2)

console.log(result.toBigInt())
Enter fullscreen mode Exit fullscreen mode

总之,千万别搞混BigNumberBigInt,否则你会倒霉的!

msg从 Ether.js设置对象

你的 Solidity 智能合约中有一些全局变量会在函数调用之前自动生成。

其中一个名为msg,它包含未通过函数参数显式传递的数据,例如msg.sender调用函数的地址或msg.value通过函数调用发送的以太币数量。

function f(uint256 arg1, uint256 arg2) public payable {
  // These are obviously arguments
  uint256 a = arg1 + arg2;

  // But where do these come from?
  address x = msg.sender; 
  uint256 y = msg.value;
}
Enter fullscreen mode Exit fullscreen mode

由于该数据不是函数参数,如何从 Ethers.js 端将其传递给智能合约?

在所有常规参数之后,覆盖对象会作为最后一个参数传递给此类(可付费)函数。其他值(例如)msg.sender则在智能合约端隐式设置。

const overrides = {value: 123}
await smartContract.payableFunction(arg1, arg2, overrides)
Enter fullscreen mode Exit fullscreen mode

在 Ethers.js 中,多个返回值将合并为一个数组。

Solidity 允许从一个函数返回多个值。

function f() public returns(uint256 a, uint256 b) {
  return (123, 456);
}
Enter fullscreen mode Exit fullscreen mode

我看到一些示例,似乎是针对 web3.js 的,它们会在 JavaScript 端使用对象作为返回值。

const {a, b} = await smartContract.f();
Enter fullscreen mode Exit fullscreen mode

这种方法对我不起作用;我使用数组根据返回值的位置提取它们。

const [a, b] = await smartContract.f();
Enter fullscreen mode Exit fullscreen mode

使用 Waffle 和 Chai 进行测试

我正在读的那本书使用了底层断言和一些 try-catch 结构来测试智能合约的特定行为。我猜那时候还没有 Waffle 这种工具吧。

要测试事件,可以使用异步调用expect

it("emits", async () => {
  await expect(smartContract.f()).to.emit("EventType")
})
Enter fullscreen mode Exit fullscreen mode

您可以使用异步调用来expect测试reverted您的合约是否能正确回滚。

it("emits", async () => {
  await expect(smartContract.f()).to.be.revertedWith("Error Message")
})
Enter fullscreen mode Exit fullscreen mode

概括

Web3是个很有意思的话题,而Solidity也确实和我预想的完全不同。它像JavaScript一样简单,但魔鬼藏在细节里。

希望我能帮您解惑。

文章来源:https://dev.to/fllstck/solidity-basics-for-javascript-devs-part-2-430e