JavaScript如何调用智能合约

2,007 阅读7分钟

准备阶段

  • 如何连接到以太坊公链上?
  • 智能合约地址是怎么来的
  • 智能合约ABI文件能干什么?
  • web3.js或ethers.js是用来做什么的?

解决了以上的问题,我们就可以很轻松的在前端项目中使用js调用智能合约实现某些功能。

如何连接到以太坊公链上?

要在主网/测试网上使用 JSON RPC 连接到以太坊节点,我们需要先能够访问以太坊节点,方法有两种:

  1. 使用GethParity运行你自己的以太坊节点
  2. 直接使用链上数据服务商对外提供的API服务如(infura

运行自己的以太坊节点需要你从区块链下载大量数据并保持同步,如果你从未这样做过那将是非常大的工作量。

本文我们采用第二种方法,具体优点在于:

  • 使用infura你不用花过多的时间去同步区块,可以直接使用
  • infura节点不掌控账户,想要发交易必须用自己的私钥签名
  • Infura是一个托管的以太坊节点集群,可以将你编写的以太坊智能合约部署到infura提供的节点上,而无需自己搭建以太坊节点

【infural使用文档】这里

智能合约地址是怎么来的?

智能合约:智能合约就是一种把我们生活中的合约数字化,当满足一定条件后,可以由程序自动执行的技术。就好比你跟我做了一个约定,我们定好奖惩措施,然后将约定通过代码的形式录入区块链中,一旦触发约定的条件,就会有程序来自动执行,这就是智能合约。

合约地址:以太坊的账户有两种,一种是个人用户使用以太坊钱包生成的外部账户,由公钥和私钥组成;一种是合约账户,使用Solidity程序语言,由一组代码(合约的函数)和数据(合约的状态)组成,比如在以太坊上发ERC20的币就是创建了一个合约账户。这种生成的地址就是合约地址,是没有私钥的。合约位于以太坊区块链上的一个特殊地址。合约地址就是一个映射着账户地址以及余额的智能合约。

智能合约ABI文件能干什么?

ABI: 计算机科学背景下的 ABI 是两个程序模块之间的接口,通常在操作系统和用户程序之间。和 API(应用程序接口)非常相似,API 是代码接口的可读表示。ABI 定义了用于与二进制合约交互的方法和结构,就像 API 一样,但在较低的级别上。ABI 指示函数的调用者以 EVM 可以理解的格式对所需的信息,如函数签名和变量声明等,进行编码,以便在字节码中调用该函数;这称为 ABI 编码。ABI 编码大部分是自动化的,由REMIX等编译器或与区块链交互的钱包处理。合约 ABI 以 JSON 格式表示。关于如何编码和解码合约 ABI 有明确的规范。

获取/生成ABI:最常见的方法之一是在智能合约编译完成后 ,使用 Ethereum REMIX IDE编译选项卡下的 ABI 按钮复制 ABI

web3.js或ethers.js是用来做什么的?

web3.js和ethers.js都是以太坊 JavaScript 库。

web3.js:是一个库集合,允许您使用 HTTP、IPC 或 WebSocket 与本地或远程以太坊节点进行交互。它允许您将以太币从一个账户发送到另一个账户、从智能合约读取和写入数据、创建智能合约等等。web3.js文档

ethers.js:与web3.js之间的一个主要区别是两者处理密钥管理以及与以太坊区块链的交互不同。eth.js将节点分成来两个不同的角色,一个保存密钥和签署交易的“钱包”,一个充当于以太坊网络的匿名连接、检查状态、发送交易的“提供者”

接下来我们来集成使用web3.js

// 安装对应的库
npm install web3

具体使用方式

// 1. 获取Web3
const Web3 = require('web3');

// 2. 确保将`YOUR_INFURA_API_KEY`“替换为您之前获得的实际 Infura API 密钥
const rpcURL = "https://mainnet.infura.io/YOUR_INFURA_API_KEY"

// 3. 实例化web3链接
const web3 = new Web3(rpcURL);

现在我们有了一个实时的web3链接对象,可以查询一些以太坊链上的数据,进行简单对话比如:

// 检查某个账户余额
const address = '0x105cb19ba40384a8f2985816DA7883b076969cA7';
web3.eth.getBalance(address, (err, wei) => {
  balance = web3.utils.fromWei(wei, 'ether')
})

注意: 以太坊以 wei 表示余额,这是 ether 的最小细分,web3.utils.fromWei(wei, 'ether')方法可以将余额转换为我们通常理解的ETH

JavaScript如何调用智能合约?

有了上述问题的理解,我们就可以来看看本文标题想要做的事情【使用JavaScript来和智能合约进行交互】

  1. 为了理解的清晰我们从这里开始重新集成web3.js、创建web3实例、建立web3链接
// 使用web3.js建立链接
const Web3 = require('web3');
const rpcURL = "https://mainnet.infura.io/YOUR_INFURA_API_KEY"
const web3 = new Web3(rpcURL);
  1. 准备一个ABI文件(这里我拿OmiseGo代币的ABI作为讲解,它支持ERC-20代币标准),如果是自己编写的智能合约可以参考上文中 获取/生成ABI方法获取对应合约的ABI内容
const abi = [{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_releaseTime","type":"uint256"}],"name":"mintTimelocked","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]
  1. Etherscan中我们可以查询到OmiseGo代币(OMG)的智能合约地址是:
const address = "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07";
  1. 现在我们可以开始创建合约链接了,通过web3.eth.Contract方法
const contract = new web3.eth.Contract(abi, address);
  1. 合约实例创建成功后我们得到了contract对象,现在我们理论上我们可以通过它调用该智能合约实现的任何功能。如果你想要知道合约内部实现什么方法,我们可以打印contract.methods。由于OmiseGo代币(OMG)智能合约实现了ERC-20 标准,因此它一定拥有的方法包含totalSupply()name()symbol()balanceOf()
//获取代币名称
contract.methods.name().call((err, result) => { console.log(result) }); // > OMG Token

//代币的总供应量
contract.methods.totalSupply().call((err, result) => { console.log(result) }); // > 140245398

以上,我们使用了web3.js库,通过Infura访问到以太坊网络中某个节点,利用该节点来访问已经部署到链上的某个智能合约中,最终我们能够和智能合约做一些简单的数据读取操作。