从网络应用程序与智能合约互动
大家好!今天我们将介绍如何构建能够与以太坊智能合约互动的Web应用程序。这种互动非常吸引人,因为它将为那些想要围绕区块链构建应用程序(dapp)的网络开发人员打开一个新的可能性世界。
在本教程中,我们将建立一个微小的智能合约来存储和检索以太坊区块链上的数据,我们将创建一个网络应用,让我们访问和改变智能合约上的数据。
该内容有两种形式,一种是涵盖整篇文章的视频讲解,你可以在这里观看。
而这篇文章以书面形式涵盖了同样的主题。视频很完整,意味着你不需要任何关于区块链的特殊知识。然而,我建议你看看我之前关于构建和部署ERC20智能合约的文章 。
智能合约
让我们从介绍智能合约开始,我们将使用它来构建我们的网络应用。由于文章的重点是将JavaScript连接到区块链上,所以我尽可能地保持合同的简单性:
pragma solidity ^0.6.6;
contract CoolNumberContract {
uint public coolNumber = 10;
function setCoolNumber(uint _coolNumber) public {
coolNumber = _coolNumber;
}
}
也许它不是最令人印象深刻的合约,但现在可以了。如果你不确定这个合约是做什么的,让我们来解释一下。
合约CoolNumberContract ,在区块链上存储了一个名为coolNumber 的变量,初始值为10 。这个变量是public ,意味着我们可以从区块链上访问它的值,而不需要建立一个getter函数。
此外,合约包含一个名为setCoolNumber 的public 函数,正如你可能猜到的,它将改变区块链上的变量值。这里真正需要记住的是,区块链数据的任何变化都需要由一个交易来表示。意味着调用方法setCoolNumber 将需要一个交易,而这个交易将有一个与之相关的气体费用。
确保你在前进之前将合约部署到一个测试网络。
设置项目和依赖关系
这里是有趣的开始。要从JavaScript中与任何以太坊区块链互动,你需要一个库,在我们的案例中,我们将使用web3。Web3将允许我们通过MetaMask或像Ganache这样的web3提供商与任何以太坊网络进行交互。
让我们开始一个新的项目;你可以使用任何你想要的框架;我将使用vanilla JavaScript和HTML,但你可以使用任何框架,如React或Vue。我所有的代码将进入一个文件,index.html ,我将从以下结构开始:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Web 3 Demo</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<script src='node_modules/web3/dist/web3.min.js'></script>
</head>
<body>
Web 3 Demo
<br >
<button onclick="printCoolNumber();">Print Cool Number</button>
<button onclick="changeCoolNumber();">Change Cool Number</button>
<br /><br />
Status: <span id="status">Loading...</span>
<script type="text/javascript">
</script>
</body>
</html>
让我们打破上面的代码,让我们从body标签开始。这是一个简单的用户界面,有两个按钮和一个代表状态的跨度。两个按钮都调用了JavaScript函数,这些函数目前还没有定义。
在head标签上,重要的标签是我们正在导入的脚本。你可以像我一样把这个依赖添加到你的代码中,或者如果你使用的是一个框架,你可能可以简单地导入这个包:
import Web3 from ‘web3’;
如果你还没有安装这个库,你可以通过NPM来完成:
npm install web3
最后但同样重要的是,在你继续之前,我强烈建议你安装MetaMask扩展。如果你喜欢使用其他的提供者,你可能要相应地修改部分代码,因为所提供的样本使用MetaMask注入的web3提供者。
将您的网络应用程序连接到以太坊区块链上
现在我们已经准备好了我们的基本结构和依赖性,我们可以添加代码,将我们的应用程序连接到区块链上。
在主体上的脚本标签内,我们将添加:
async function loadWeb3() {
if (window.ethereum) {
window.web3 = new Web3(window.ethereum);
window.ethereum.enable();
}
}
async function load() {
await loadWeb3();
updateStatus('Ready!');
}
function updateStatus(status) {
const statusEl = document.getElementById('status');
statusEl.innerHTML = status;
console.log(status);
}
load();
上面所有的代码都很简单,除了函数loadWeb3 ,我们将进一步解释。这个函数负责建立连接并授权我们与区块链互动。
为了与我们的智能合约一起工作,我们将需要一个新的实例Web3 。在创建这个实例时,我们需要指定我们要使用的提供者。由于我们使用MetaMask作为代理,我们使用由MetaMask扩展注入的window.ethereum 提供者。
如果你现在去浏览器加载页面(通过文件或网络浏览器),你会看到MetaMask的授权流程。它看起来应该是这样的。

授权你的应用程序通过MetaMask进行连接
请确保接受并将钱包连接到你的应用程序,以继续。
访问智能合约
到目前为止,你的代码已经可以与区块链互动;现在让我们确保你的应用程序可以与智能合约对话。
为此,我们将创建一个新的函数来创建一个与你的合约接口相匹配的合约实例。
async function loadContract() {
return await new window.web3.eth.Contract(ABI, contractAddress);
}
为了获得区块链上任何合约的实例,我们所需要的是两件事。合同的ABI规范和合同地址,这两样东西你都可以从Remix中提取。
要获得合同的ABI规范,请进入Remix的编译标签,确保编译,然后点击ABI 。

复制合同的ABI规范
这个按钮将把您的合同的ABI规范复制成JSON数组放在剪贴板上,我们可以直接使用它作为我们第一个参数的一部分。
第二个参数是部署的合同地址,您可以在部署时从Remix或Etherscan获得:

从Remix复制合同地址

从Etherscan中复制合同地址
以下是我的合同的完整功能代码:
async function loadContract() {
return await new window.web3.eth.Contract([
{
"inputs": [],
"name": "coolNumber",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_coolNumber",
"type": "uint256"
}
],
"name": "setCoolNumber",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
], '0x5F4a8C71AFB0c01BA741106d418E78888607Ee63');
}
完成这些后,我们可以简单地从我们的加载器函数中调用loadContract:
async function load() {
await loadWeb3();
window.contract = await loadContract();
updateStatus('Ready!');
}
从智能合约中读取数值
我们已经准备好开始调用智能合约的功能,我们将首先从合约中检索我们的coolNumber 。
由于web3的存在,我们可以非常快速地从合同中检索数据;这里有一个例子,可以获得公共变量coolNumber:
async function printCoolNumber() {
updateStatus('fetching Cool Number...');
const coolNumber = await window.contract.methods.coolNumber().call();
updateStatus(`coolNumber: ${coolNumber}`);
}
超级简单!我们使用上一节中的contract 实例,我们获得方法并调用一个带有变量名称的函数(这是我在开始时提到的getter),最后,我们使用call 来启动远程请求。
更新智能合约的值
最后,我们需要确保我们也能与智能合约进行交易,为此,我们将通过访问我们的setter函数来改变存储在合约中的coolNumber ,展示一个例子。
我们的改变函数将简单地分配一个新的随机数并将其保存在区块链上:
async function changeCoolNumber() {
const value = Math.floor(Math.random() * 100);
updateStatus(`Updating coolNumber with ${value}`);
const account = await getCurrentAccount();
const coolNumber = await window.contract.methods.setCoolNumber(value).send({ from: account });
updateStatus('Updated.');
}
这里我想强调两件事。首先,我们引用了一个getCurrentAccount() 函数,这个函数目前还没有定义;我们稍后将对此进行研究。第二是我们如何调用我们的setter。如果你注意一下我们从合同中调用setCoolNumber 方法的那一行,它看起来与我们对调用者所做的略有不同。
const coolNumber = await window.contract.methods.setCoolNumber(value).send({ from: account });
我们没有使用call ,而是使用了send 方法。我们需要指定发送者的账户。为什么呢?事实证明,我们需要一个交易来改变区块链上的值。说到这里,一个交易需要一个from和to账户才有效,from是谁发起的交易,to,在这里是智能合约的地址。
我们可以使用任何账户作为起始值吗?不可以,必须是你可以访问的账户(在我们的例子中,在你的MetaMask钱包上注册),因为你需要授权交易并确认加油费。
现在我们清除了这一点,让我们建立getCurrentAccount() 方法:
async function getCurrentAccount() {
const accounts = await window.web3.eth.getAccounts();
return accounts[0];
}
Web3是很好的。我们可以与区块链和我们的钱包进行互动,因此可以通过Web3来请求钱包上注册的账户信息。在我们的例子中,我们只是简单地得到它们,并使用第一个账户来进行交易。
把所有的东西放在一起
如果你完成了所有的功能,你的代码应该看起来像我的。我已经把我的代码上传到GitHub的Gist上,这样你就可以和你的代码进行比较,或者用我的代码来玩。
我所有的联系方式都在代码中,只要你把你的MetaMask连接到Ropsten,并有一些测试ETH来支付交易费用,你就可以使用它。
测试时,你应该看到类似的东西:

应用流程
结论
区块链是一个让我着迷的话题,建立这个教程超级有趣;我在做这个的时候学到了很多,它是更多项目、文章和视频的基础。
如果有关于智能合约开发的话题,你希望我介绍,请在评论区告诉我。
谢谢你的阅读!