你好!今天,我们将介绍如何构建能够与以太坊智能合约交互的web应用程序。这种互动非常吸引人,因为它将为那些希望围绕区块链构建应用程序(dapp)的web开发者打开一个新的可能性世界。
在本教程中,我们将构建一个微型智能合约来存储和检索以太坊区块链上的数据,并创建一个允许我们访问和更改智能合约上数据的web应用程序。
**智能合约**
让我们先介绍一下我们将用于构建web应用程序的智能合约。由于本文的重点是将JavaScript连接到区块链,因此我尽量简化合合约:
pragma solidity ^0.6.6;
contract CoolNumberContract {
uint public coolNumber = 10;
function setCoolNumber(uint _coolNumber) public {
coolNumber = _coolNumber;
}
}
也许这并不是有史以来最令人印象深刻的智能合约,但现在就可以了。如果你不确定这个智能合约是干什么的,让我解释一下。
智能合约CoolNumberContract在区块链上存储一个名为coolNumber的变量,初始值为10。这个变量是公共函数,这意味着我们可以从区块链访问它的值,而无需构建getter函数。
此外,智能合约还包含一个名为setCoolNumber的公共函数,您可能已经猜到了,它将更改区块链上变量的值。这里要记住的真正重要的一点是,区块链数据中的任何更改都必须由事务来表示。这意味着调用方法setCoolNumber将需要进行交易,并且该交易将具有与之相关的gas费。
在继续进行之前,请确保将智能合约部署到测试网络。
设置项目和依赖项
这就是乐趣的开始。要与JavaScript中的任何以太坊区块链进行交互,您将需要一个库。在我们的例子中,我们将使用web3。Web3将允许我们通过MetaMask或Gan3之类的web3提供程序与任何以太坊网络进行交互。
让我们开始一个新项目。您可以使用任何所需的框架。我将使用原始的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>
Change Cool Number
<br /><br />
Status: Loading…
<script type=”text/javascript”>
</script>
</body>
</html>
让我们分解上面的代码,从body标签开始。这是一个简单的用户界面,有两个按钮和一个表示状态的范围。这两个按钮都调用目前尚未定义的JavaScript函数。
在head标签上,重要的标签是我们正在导入的脚本。那就是我们对web3的依赖。您可以像我一样将这种依赖关系添加到代码中,或者,如果您使用的是框架,则可以使用以下命令简单地导入包:
import Web3 from ‘web3’;
如果尚未安装该库,则可以通过NPM进行:
npm install web3
最后在继续之前,我强烈建议您安装MetaMask扩展。如果您喜欢使用任何其他提供程序,则可能必须相应地更改部分代码,因为所提供的示例使用MetaMask注入的web3提供程序。
将Web应用程序连接到以太坊区块链
现在我们已经准备好基本结构和依赖项,我们可以添加将应用程序连接到区块链的代码。
在正文的script标签内,我们将添加:
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提供程序。
如果现在转到浏览器并通过文件或Web浏览器加载页面,您将看到MetaMask授权流程。它看起来应该像这样:
确保接受并将钱包连接到您的应用程序以继续。
访问智能合约
到目前为止,您的代码可以访问与区块链的交互。确保您的应用可以与智能合约进行交易。
为此,我们将创建一个新函数来创建与您的合约接口匹配的合约实例:
async function loadContract() {
return await new window.web3.eth.Contract(ABI, contractAddress);
}
要获得区块链上任何合约的实例,我们需要做的两件事:合约的ABI规范和合约地址,您都可以从Remix中提取两者。
要获取合约的ABI规范,请转到“编译”选项卡上的“Remix”,确保进行编译,然后单击“ ABI”:
此按钮会将您的合约的ABI规范复制为剪贴板上的JSON数组,我们可以将其直接用作第一个参数的一部分。
第二个参数是已部署的合约地址,您可以在部署时从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账户是有效的-从是交易的发起人到,在本例中,是智能合约地址。
我们可以将任何帐户用作起始值吗?不,它必须是您有权访问的帐户(在我们的情况下,该帐户已在您的MetaMask钱包中注册),因为您将需要授权交易并确认gas费。
现在我们已经清除了这一点,让我们构建getCurrentAccount()方法:
async function getCurrentAccount() {
const accounts = await window.web3.eth.getAccounts();
return accounts[0];
}
Web3很棒。我们可以与区块链和钱包进行交互,因此可以通过Web3请求有关在钱包上注册的帐户的信息。在我们的示例中,我们只需将它们全部拿走并使用第一个帐户进行交易即可。
把它们放在一起
如果您完成了所有功能,则代码应类似于我的代码。测试时,您应该会看到类似以下内容的内容: