使用truffle初学eth合约开发
直接打开 http://trufflesuite.com/tutorial/index.html 有新手指导区,英语好的老板可以直接参考原文,下面是根据教程加上我的体验进行的翻译和实践记录。
这个教程可以让你体验做一个dapp宠物商店(pet shop),通过这个教程可以完整的体验一次合约的开发。
这个教程主要会让我们体验这么几个过程
需要安装
上述两个安装完成之后,npm安装 truffle
# 安装truffle
npm install -g truffle
# 确认是否安装完成
truffle version
# 新建文件夹
mkdir pet-shop-tutorial
# 进入文件夹
cd pet-shop-tutorial
# 使用truffle 创建项目框架
truffle unbox pet-shop
ps:介于国内的网络原因,这一步会经常失败,老板可以考虑科学上网,或者失败之后多运行几次,总有成功的。等不了的可以直接去clone他的远嘛到该目录 https://github.com/trufflesuite/pet-shop-tutorial
成功之后就有如下的目录结构
挑选主要文件讲解一下
├── LICENSE
├── contracts #包含我们智能合约的 Solidity 源文件。这里有一个重要的契约叫做Migrations.sol,后续教程会讲到
├── migrations #Truffle 使用迁移系统来处理智能合约部署。迁移是一个额外的特殊智能合约,用于跟踪更改。
├── src
├── test # 包含我们智能合约的 JavaScript 和 Solidity 测试
└── truffle-config.js # 配置文件
1.在 contracts
文件夹下面创建 Adoption.sol
2.添加如下内容到新建的文件
# 注明了 solidity的合约最低版本,^ 表示需要更高的版本,即向上兼容
pragma solidity ^0.5.0;
contract Adoption {
}
Solidity 是一种静态类型语言,这意味着必须定义字符串、整数和数组等数据类型。 Solidity 有一种独特的类型,称为地址。地址是以太坊地址,存储为 20 字节值。以太坊区块链上的每个账户和智能合约都有一个地址,可以从该地址发送和接收以太币。
pragma solidity ^0.5.0;
contract Adoption {
address[16] public adopters;
}
// Adopting a pet
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
// Retrieving the adopters
function getAdopters() public view returns (address[16] memory) {
return adopters;
}
Solidity是一种编译语言,意味着我们需要将我们的Solidity编译成字节码,以便Ethereum虚拟机(EVM)执行。把它看作是把我们的人类可读的Solidity翻译成EVM能理解的东西。 在终端中,确保你在包含dapp的目录的根部,并输入。
truffle compile
现在,我们已经成功地编译了我们的合约,是时候将它们迁移到区块链上了!这就是我们的工作。
你会看到在migrations/
目录下已经有一个JavaScript文件。1_initial_migration.js
。这是处理部署Migrations.sol合约以观察后续的智能合约迁移,并确保我们在未来不会重复迁移未改变的合约。
现在我们准备创建我们自己的迁移脚本。
1.在migrations/
目录下创建一个名为2_deploy_contracts.js
的新文件。
2.在2_deploy_contracts.js
文件中添加以下内容。
var Adoption = artifacts.require("Adoption");
module.exports = function(deployer) {
deployer.deploy(Adoption);
};
下载地址:http://trufflesuite.com/ganache
Ganache是一个快速启动个人以太坊区块链,您可以使用它来运行测试、执行命令和检查状态,同时控制链的运行方式。如果不用他,直接在真实的网络部署测试的话,是需要花费eth的。安装这个在本地就不需要了。
这个很简单,下载安装就好,然后双击运行,就可以看到本地跑起来了。
本地的端口跑在 HTTP://127.0.0.1:7545
使用 lsof -i:7544
确认已经成功运行
truffle migrate
然后你就会得到一堆迁移部署的消息
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'development'
> Network id: 5777
> Block gas limit: 6721975 (0x6691b7)
1_initial_migration.js
======================
Replacing 'Migrations'
----------------------
> transaction hash: 0xf848aad72fb6ab403f201ff2fa1113d959d1ecb7fe331260752c263925ca403c
> Blocks: 0 Seconds: 0
> contract address: 0xC0112c7071c9Fab2f42D438B738D51b367fca38a
> block number: 1
> block timestamp: 1638962361
> account: 0x4035a5D4709fD2Cb87160Bd29ca5eD88C9E5765F
> balance: 99.99616114
> gas used: 191943 (0x2edc7)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00383886 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00383886 ETH
2_deploy_contracts.js
=====================
Replacing 'Adoption'
--------------------
> transaction hash: 0xabfd61f1d738ab26c5c0f65ee9cc37787d3cc46656cf9ce59c559affeabbb71b
> Blocks: 0 Seconds: 0
> contract address: 0x705b3e834149c2eF5bbE00440474f0aC3327451A
> block number: 3
> block timestamp: 1638962361
> account: 0x4035a5D4709fD2Cb87160Bd29ca5eD88C9E5765F
> balance: 99.99123784
> gas used: 203827 (0x31c33)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00407654 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00407654 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0.0079154 ETH
最终花费了0.0079154的eth,如果直接在线上的话就需要花钱了。。
实例化web3
在文本编辑器中打开/src/js/app.js
检查该文件。注意,有一个全局的App对象来管理我们的应用程序,在init()中加载宠物数据,然后调用函数initWeb3()
。web3的JavaScript库与以太坊区块链进行互动。它可以检索用户账户,发送交易,与智能合约互动,等等。
web3的api可以参考 https://github.com/ethereum/web3.js/
这段代码本质上就是先获得一个web3的对象,然后通过该web3api与合约进行交互,大部分都是封装好了的api。
最开始我们写了2个合约的方法function adopt
function getAdopters()
定义了一个类型为address的变量adopters
1.初始化web3
2.初始化合约。初始化合约的时候载入Adoption.json
这个文件,该文件是我们执行truffle compile
生成的。这个文件包含了合约的工程文件(artifact file),Artifacts是关于我们合同的信息,比如它的部署地址和应用二进制接口(ABI)。ABI是一个JavaScript对象,定义了如何与合同互动,包括其变量、函数及其参数。( Artifacts are information about our contract such as its deployed address and Application Binary Interface (ABI). The ABI is a JavaScript object defining how to interact with the contract including its variables, functions and their parameters.)
3.markAdopted
判断是否已经被买了,通过adoptionInstance.getAdopters.call();
调用所有记录的adopters地址,如果不是初始化的地址,就标记为success,并且disable。返回的合约信息如下图:
4.bindEvents
绑定事件,如果可以购买,则触发领养 App.contracts.Adoption.deployed(),然后刷新UI即可。
// app.js
App = {
web3Provider: null,
contracts: {},
init: async function () {
// Load pets.
$.getJSON("../pets.json", function (data) {
var petsRow = $("#petsRow");
var petTemplate = $("#petTemplate");
for (i = 0; i < data.length; i++) {
petTemplate.find(".panel-title").text(data[i].name);
petTemplate.find("img").attr("src", data[i].picture);
petTemplate.find(".pet-breed").text(data[i].breed);
petTemplate.find(".pet-age").text(data[i].age);
petTemplate.find(".pet-location").text(data[i].location);
petTemplate.find(".btn-adopt").attr("data-id", data[i].id);
petsRow.append(petTemplate.html());
}
});
return await App.initWeb3();
},
initWeb3: async function () {
// Modern dapp browsers...
if (window.ethereum) {
App.web3Provider = window.ethereum;
try {
// Request account access
await window.ethereum.request({ method: "eth_requestAccounts" });
} catch (error) {
// User denied account access...
console.error("User denied account access");
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = window.web3.currentProvider;
}
// If no injected web3 instance is detected, fall back to Ganache
else {
App.web3Provider = new Web3.providers.HttpProvider(
"http://localhost:7545"
);
}
web3 = new Web3(App.web3Provider);
return App.initContract();
},
initContract: function () {
$.getJSON("Adoption.json", function (data) {
// Get the necessary contract artifact file and instantiate it with @truffle/contract
var AdoptionArtifact = data;
App.contracts.Adoption = TruffleContract(AdoptionArtifact);
// Set the provider for our contract
App.contracts.Adoption.setProvider(App.web3Provider);
// Use our contract to retrieve and mark the adopted pets
return App.markAdopted();
});
return App.bindEvents();
},
bindEvents: function () {
$(document).on("click", ".btn-adopt", App.handleAdopt);
},
markAdopted: function () {
var adoptionInstance;
App.contracts.Adoption.deployed()
.then(function (instance) {
adoptionInstance = instance;
return adoptionInstance.getAdopters.call();
})
.then(function (adopters) {
for (i = 0; i < adopters.length; i++) {
if (adopters[i] !== "0x0000000000000000000000000000000000000000") {
$(".panel-pet")
.eq(i)
.find("button")
.text("Success")
.attr("disabled", true);
}
}
})
.catch(function (err) {
console.log(err.message);
});
},
handleAdopt: function (event) {
event.preventDefault();
var petId = parseInt($(event.target).data("id"));
var adoptionInstance;
web3.eth.getAccounts(function (error, accounts) {
if (error) {
console.log(error);
}
var account = accounts[0];
App.contracts.Adoption.deployed()
.then(function (instance) {
adoptionInstance = instance;
// Execute adopt as a transaction by sending account
return adoptionInstance.adopt(petId, { from: account });
})
.then(function (result) {
return App.markAdopted();
})
.catch(function (err) {
console.log(err.message);
});
});
},
};
$(function () {
$(window).load(function () {
App.init();
});
});
先安装metamask插件,可以在chrome插件商城安装。
如果有了直接点击添加网络
按照如下资料进行填写
这里的http://127.0.0.1:7545
就是你本地启动Gnanche的时候启动的
填写完成之后,选择网络,把网络切换到本地测试网络
点击小狐狸插件头像,切换网络即可
切换到本地网络之后,可以直接倒入本地的账号
点击钥匙图标,查看本地的私钥,然后选择小狐狸直接倒入。
倒入成功之后,你账号就有了99.99eth,其他账号没有消耗的话应该都是100ETH
回到开发目录下
npm run dev
首个DAPP就跑起来了。点击adopt就可以唤起小狐狸进行领养和gas支付了。
那么至此,基本上就了解了一个DAPP的运行逻辑了。
梳理一下,就是我们编写了一个solidity的合约,需要先编译成机器码,然后再部署到ETH的网络,部署的时候需要消耗GAS。
部署完成之后,我们就可以通过用户UI去进行交互,调用metamask去与底层web3API进行交流。比如这个demo就是领养宠物,在游戏打金领域就是氪金挖矿了。当然我们可以直接跳过这些,直接用web3去跟合约交流了。
更深入的了解需要学习一下solidity的语法,然后根据游戏去挖掘相应合约的方法调用即可。
可以继续参考老板的未公开合约的游戏怎么制作脚本 思路和方法已经讲明白了,剩下的就靠自己多领悟和学习了
既然本地已经有了eth的网络环境了,那么我是不是可以通过web3 api直接批量创建钱包地址呢?
之前一直想看老板批量创建metamask账号的的代码,可惜权限不够,这回理解了应该可以自己动手了~
https://web3js.readthedocs.io/en/v1.2.11/web3-eth-accounts.html#example
查看API,有一个account的接口。通过以下代码即可创建一个账号,后续再通过循环就可以创建和存储了。
//通过web3 api 创建账号
const accepts = require('accepts');
var Web3 = require('web3');
web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:7545"));
let account = web3.eth.accounts.create('test');
let keystore = account.encrypt('test');
console.log(account, keystore);
/***
{
address: '0x3E12932eF648bE841e03db1CE46Faec4aFc9AAe1',
privateKey: '0x4291e1585205b5a0217ecea837f2070ea2100a401f753806c15a93dfb84c3661',
signTransaction: [Function: signTransaction],
sign: [Function: sign],
encrypt: [Function: encrypt]
} {
version: 3,
id: '8a2ded2a-8469-43d4-88a0-3e4169012d54',
address: '3e12932ef648be841e03db1ce46faec4afc9aae1',
crypto: {
ciphertext: 'e7492608eb06ee703909b6348054f8b98bc30a79a5d4628fad72e012c4a50075',
cipherparams: { iv: 'a2b68822d9b03e788ad8df3a57c95cec' },
cipher: 'aes-128-ctr',
kdf: 'scrypt',
kdfparams: {
dklen: 32,
salt: 'f513ee04af1662d1adab37e8962423d9d4ff65218aadce7b88063eb6e6d1ece0',
n: 8192,
r: 8,
p: 1
},
mac: 'eadc2bb27d5fe6c8889caee2f897eabde4cd9254005af9bdb11f5c7989fe6837'
}
}
***/
假设我已经创建了一个 0x3E12932eF648bE841e03db1CE46Faec4aFc9AAe1
的新账号,
然后我本地导入metamask的账号已经有了99.99eth,我想给新账号转账。
直接通过metamask转账即可。
转账成功,可以用代码查询新建账号的余额,查询成功~
var Web3 = require('web3');
//创建 rpc 连接字符串
var rpcstring = 'http://127.0.0.1:7545'
//创建ws连接字符串
var wstring = 'wss://bsc-ws-node.nariox.org:443';
// var wscweb3 = new Web3(new Web3.providers.WebsocketProvider(wstring ));
var rpcweb3 = new Web3(new Web3.providers.HttpProvider(rpcstring ));
//设置web3 使用rpcweb3模式
web3 = rpcweb3;
const getBNBBalance = async (address) =>
{
let result = await web3.eth.getBalance(address)
//由于使用的是大数模式,小数点有18位,所以获得的balance 要除以10^18次方才是正确的数据
//或者使用自带的转换工具
let balance = web3.utils.fromWei(result.toString(10), getweiname());
//打印结果
console.log("地址:" + address +"有" + balance +"个ETH");
return balance;
}
//通过小数点多少位,转换对应的数据
function getweiname(tokendecimals = 18) {
weiname = 'ether';
switch (tokendecimals) {
case 3:
weiname = "Kwei";
break;
case 6:
weiname = 'mwei';
break;
case 9:
weiname = 'gwei';
break;
case 12:
weiname = 'microether ';
break;
case 15:
weiname = 'milliether';
break;
case 18:
weiname = 'ether';
break;
}
return weiname;
}
let banlance = getBNBBalance('0x3E12932eF648bE841e03db1CE46Faec4aFc9AAe1');
console.log('banlance:' + banlance);
//banlance:[object Promise]
//地址:0x3E12932eF648bE841e03db1CE46Faec4aFc9AAe1有88个ETH
可以再试试通过web3api实现批量转账等测试,等有时间再试试。作为小白,通过这个教程大致梳理了一下整个DAPP的开发流程,如果有纰漏也请各位老板指正。感谢感谢~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!