赞
踩
以太坊基金发布的 JavaScript 库 — Web3.js
以太坊网络是由节点组成的,每一个节点都包含了区块链的一份拷贝。当你想要调用一份智能合约的一个方法,你需要从其中一个节点中查找并告诉它:
智能合约的地址
你想调用的方法,以及
你想传入那个方法的参数
以太坊节点只能识别一种叫做 JSON-RPC 的语言。这种语言直接读起来并不好懂。当你你想调用一个合约的方法的时候,需要发送的查询语句将会是这样的:
{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","gas":"0x76c0","gasPrice":"0x9184e72a000","value":"0x9184e72a","data":"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}],"id":1}
可以从 github 直接下载压缩后的 .js 文件 然后包含到你的项目文件中:
<script language="javascript" type="text/javascript" src="web3.min.js"></script>
在 Web3.js 里设置 Web3 的 Provider(提供者) 告诉我们的代码应该和 哪个节点 交互来处理我们的读写。这就好像在传统的 Web 应用程序中为你的 API 调用设置远程 Web 服务器的网址。
metamask
window.addEventListener('load', function() {
// 检查web3是否已经注入到(Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// 使用 Mist/MetaMask 的提供者
web3js = new Web3(web3.currentProvider);
} else {
// 处理用户没安装的情况, 比如显示一个消息
// 告诉他们要安装 MetaMask 来使用我们的应用
}
// 现在你可以启动你的应用并自由访问 Web3.js:
startApp()
})
Web3.js 需要两个东西来和你的合约对话: 它的 地址 和它的 ABI。
部署后复制这个地址以来和你的智能合约对话
ABI 意为应用二进制接口(Application Binary Interface)。 基本上,它是以 JSON 格式表示合约的方法,告诉 Web3.js 如何以合同理解的方式格式化函数调用。
实例化:
var myContract = new web3js.eth.Contract(myABI, myContractAddress);
Web3.js 有两个方法来调用我们合约的函数: call and send.
call 用来调用 view 和 pure 函数。它只运行在本地节点,不会在区块链上创建事务。
复习: view 和 pure 函数是只读的并不会改变区块链的状态。它们也不会消耗任何gas。用户也不会被要求用MetaMask对事务签名。
send 将创建一个事务并改变区块链上的数据。你需要用 send 来调用任何非 view 或者 pure 的函数。
注意: send 一个事务将要求用户支付gas,并会要求弹出对话框请求用户使用 Metamask 对事务签名。在我们使用 Metamask 作为我们的 web3 提供者的时候,所有这一切都会在我们调用 send() 的时候自动发生。而我们自己无需在代码中操心这一切,挺爽的吧。
如何显示你从合约获取的数据
function displayZombies(ids) { $("#zombies").empty(); for (id of ids) { // Look up zombie details from our contract. Returns a `zombie` object getZombieDetails(id) .then(function(zombie) { // Using ES6's "template literals" to inject variables into the HTML. // Append each one to our #zombies div $("#zombies").append(`<div class="zombie"> <ul> <li>Name: ${zombie.name}</li> <li>DNA: ${zombie.dna}</li> <li>Level: ${zombie.level}</li> <li>Wins: ${zombie.winCount}</li> <li>Losses: ${zombie.lossCount}</li> <li>Ready Time: ${zombie.readyTime}</li> </ul> </div>`); }); } }
相对 call 函数,send 函数有如下主要区别:
send 一个事务需要一个 from 地址来表明谁在调用这个函数(也就是你 Solidity 代码里的 msg.sender )。 我们需要这是我们 DApp 的用户,这样一来 MetaMask 才会弹出提示让他们对事务签名。
send 一个事务将花费 gas
在用户 send 一个事务到该事务对区块链产生实际影响之间有一个不可忽略的延迟。这是因为我们必须等待事务被包含进一个区块里,以太坊上一个区块的时间平均下来是15秒左右。如果当前在以太坊上有大量挂起事务或者用户发送了过低的 gas 价格,我们的事务可能需要等待数个区块才能被包含进去,往往可能花费数分钟。
啥是 Wei?
一个 wei 是以太的最小单位 — 1 ether 等于 10^18 wei
太多0要数了,不过幸运的是 Web3.js 有一个转换工具来帮我们做这件事:
// 把 1 ETH 转换成 Wei
web3js.utils.toWei("1", "ether");
使用 indexed
为了筛选仅和当前用户相关的事件,我们的 Solidity 合约将必须使用 indexed 关键字
查询过去的事件
我们甚至可以用 getPastEvents 查询过去的事件,并用过滤器 fromBlock 和 toBlock 给 Solidity 一个事件日志的时间范围(“block” 在这里代表以太坊区块编号):
cryptoZombies.getPastEvents("NewZombie", { fromBlock: 0, toBlock: 'latest' })
.then(function(events) {
// events 是可以用来遍历的 `event` 对象
// 这段代码将返回给我们从开始以来创建的僵尸列表
});
因为你可以用这个方法来查询从最开始起的事件日志,这就有了一个非常有趣的用例: 用事件来作为一种更便宜的存储。
Web3.js 事件 和 MetaMask
上面的示例代码是针对 Web3.js 最新版1.0的,此版本使用了 WebSockets 来订阅事件。
但是,MetaMask 尚且不支持最新的事件 API (尽管如此,他们已经在实现这部分功能了, 点击这里 查看进度)
所以现在我们必须使用一个单独 Web3 提供者,它针对事件提供了WebSockets支持。 我们可以用 Infura 来像实例化第二份拷贝:
var web3Infura = new Web3(new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws"));
var czEvents = new web3Infura.eth.Contract(cryptoZombiesABI, cryptoZombiesAddress);
然后我们将使用 czEvents.events.Transfer 来监听事件,而不再使用 cryptoZombies.events.Transfer。我们将继续在课程的其他部分使用 cryptoZombies.methods。
将来,在 MetaMask 升级了 API 支持 Web3.js 后,我们就不用这么做了。但是现在我们还是要这么做,以使用 Web3.js 更好的最新语法来监听事件。
这里是你要完整实现所需要完成的基本事项列表:
为 attack, changeName, changeDna 以及 ERC721 函数 transfer, ownerOf, balanceOf 等实现前端函数。这些函数的实现将和我们讲过的 send事务的函数非常相似。
实现一个“管理界面”,在那里你可以调用 setKittyContractAddress, setLevelUpFee, 以及 withdraw。再次,在前端这块没有什么特别的代码——这些实现之间将非常相似。你应该保证从部署合同时候相同的以太坊地址调用这些函数,因为他们都有onlyOwner 修饰符。
在应用里我们还应该实现一些其他的界面:
a. 一个僵尸页面,在那里你可以查看一个特定僵尸的信息并可以分享它的链接。这个页面应该渲染僵尸的外形,展示它的名字,它的所有者(以及用户主页的链接),它的输赢次数,它的战斗记录等等。
b. 一个用户界面,在那里你可以查看用户的僵尸大军并分享它的链接。
c. 一个主页,就是用户页面的变体,可以展示当前用户的僵尸大军(正如我们在index.html)里面实现的那样。
界面中的一些方法允许用户用 CryptoKitties 喂食僵尸。我们可以给每一个僵尸添加一个按钮,叫做“给我投食”,再给一个输入框让用户输入一个猫咪的ID(或者一个猫咪的网址,比如https://www.cryptokitties.co/kitty/578397),它将触发我们的 feedOnKitty 函数。
界面中的一些方法将让用户用来攻击其他用户的僵尸
实现这点的一个方法是,当用户浏览其他用户的页面的时候,可以在对方僵尸旁边显示一个按钮,叫做“攻击这头僵尸”。当用户点击的时候,可以弹出一个模块,展示当前用户的僵尸大军并询问用户“你想用哪头僵尸出战?”
在用户的主页,也可以在每个僵尸旁边显示一个按钮,叫做“攻击一个僵尸”。当用户点击的时候,可以弹出一个模块,展示一个搜索框,可以让用户输入僵尸ID或者网址来搜索,或者也可以有一个按钮叫做“随机攻击一头僵尸”,将随机搜索一头僵尸来。
我们也建议你将在冷却期的僵尸用特殊的颜色显示,比如使其变成灰色。这样界面就能告诉用户不能用冷却期的僵尸来进攻。
在用户的主页,每一个僵尸也应该有选项可以更改名字、DNA、以及升级(通过付费)。若用户等级不到,无法使用的选项应该标灰。
对于新用户,我们应该显示一个欢迎信息,并让其确认用 createRandomZombie()创建一个新僵尸。
也可以为我们的智能合约添加一个包含indexed 的用户地址属性的 Attack 事件。这样就可以创建实时通知了——我们可以在用户的僵尸遭受攻击的时候弹出一条通知,这样他们可以看到谁在用什么僵尸攻击他们并做出报复。
我们也许还想实现一些前端缓存层,这样就不用总是为了相同的数据去访问Infura。(在我们当前实现中,displayZombies 将在每次页面刷新的时候为每一个僵尸调用 getZombieDetails,但是实际中我们将只需要为新加入的僵尸调用这个函数)
一个实时聊天室,这样你就可以在你击溃别人的僵尸大军的同时嘲讽他们?
因为这将需要大量的前端代码来实现全部的界面(HTML, CSS, JavaScript 以及诸如 React 和 Vue.js 这样的框架)。光实现一个这样的前端界面也许会花费多达10节课,所以我们将这个光荣的任务交给你自己去完成。
注意:尽管智能合约是去中心化的。这个用来和DApp交互的前端界面依然需要放在我们中心化的网络服务器上。不过,有了我们正在内测的Loom Network SDK,很快你就可以在应用自己的DApp链上运行前端界面而不是中心化的网络服务器。这样在以太坊和 Loom DApp 链上,你的整个应用都100%运行在区块链上了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。