Truffle
Truffle Suite - Truffle Suite
快速入门 Truffle | Truffle 中文文档 - DApp 开发框架 | 深入浅出区块链 (learnblockchain.cn)
Truffle is the most popular development framework for Ethereum with a mission to make your life a whole lot easier
只要记住 truffle是只能合约的一个开发框架就好了。
Features
- 管理智能合约的生命周期
- 自动化合约测试
- 可编程,可部署,可发布合约
- 不用过多的关注网络管理
- 强大的交互式控制台
Ganache
Ganache下载
快速启动以太坊区块私有链,可以使用它来运行测试、执行命令和检查状态,同时控制链的运行方式。
Dapp Pet-Shop 教程
Truffle tutorial
- 设置开发环境
- 通过Truffle Box创建一个Truffle项目
- 编写智能合约
- 编译(compile)和迁移(migrate)智能合约
- 测试合约
- 创建对外接口
- 浏览器中与app交互
开发环境
PS C:\Users\simon> node -v
v14.17.1
PS C:\Users\simon> npm -v
6.14.13
PS C:\Users\simon> truffle.cmd version
Truffle v5.4.29 (core: 5.4.29)
Solidity v0.5.16 (solc-js)
Node v14.17.1
Web3.js v1.5.3
npm 安装
Node.js 安装配置
Git 安装
Git (git-scm.com)
truffle 安装
PS C:\Users\simon> npm install -g truffle
PS C:\Users\simon> truffle.cmd version
Truffle v5.4.29 (core: 5.4.29)
Solidity v0.5.16 (solc-js)
Node v14.17.1
Web3.js v1.5.3
下载source
git clone https://github.com/truffle-box/pet-shop-box.git
目录结构:
- contracts/ : 存放solidity智能合约文件
- migrations/ : truffle使用migration system 来控制合约的部署。 migration 是一种额外的特殊智能合约,用来合约的维护和更改
- test/ : 测试文件存放文字(javascript or solidity)
- truffle-config.js : 配置文件
- src/: node代码
编写智能合约
- 在contracts 文件夹下新建 Adoption.sol文件,内容如下
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.8.0;
contract Adoption {
// 声明一个数组
address[16] public adopters;
// 创建收养宠物函数
function adopt(uint petId) public returns (uint){
// 校验 petId必须为 0 - 15位
require(petId >= 0 && petId <= 15);
// 收养账户为 消息发送者的地址
adopters[petId] = msg.sender;
// 返回收养的宠物ID
return petId;
}
// 从内容存中获取 收养账户信息
// memory 给出变量的来源.
// 函数中view 关键字,意味着不会改变合约的状态.
function getAdopters() public view returns (address[16] memory) {
return adopters;
}
}
编译合约
- 打开命令行控制台,进行项目根目录下
PS E:\study\truffle\pet-shop-box> truffle compile
Compiling your contracts...
===========================
> Compiling .\contracts\Adoption.sol
> Compiling .\contracts\Adoption.sol
> Compiling .\contracts\Migrations.sol
> Artifacts written to E:\study\truffle\pet-shop-box\build\contracts
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
Migration
从上面的compile成功,我们可以将它移动到区块链中
migration 是一个部署脚本,它将修改您的应用合约的状态,使它从一个状态变成另一个。对于第一次migration,你仅仅需要部署新代码即可。随着时间的迁移,后面的迁移可能会改变数据或者使用新的合约替换它。
在 migrations 文件夹下,你会看到1_initial_migration.js 这个合约是部署Migrations.sol的,用于观察后续智能合约迁移,并确保我们将来不会重复迁移未更改的合约。
- 新增migration 脚本
在migrations文件夹 下新增 2_deploy_contracts.js
var Adoption = artifacts.require("Adoption");
module.exports = function(deployer) {
deployer.deploy(Adoption);
};
在运行执行,首先咱们将ganache打开,ganache quickly start 会运行在 默认7545端口
- 执行migration脚本
PS E:\study\truffle\pet-shop-box> truffle migrate
1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
> transaction hash: 0x3b558e9cdf1231d8ffb3445cb2f9fb01de9d0363e0b97a17f9517da318c2e5af
> Blocks: 0 Seconds: 0
> contract address: 0x5ccb4dc04600cffA8a67197d5b644ae71856aEE4
> account: 0x8d9606F90B6CA5D856A9f0867a82a645e2DfFf37
> balance: 99.99430184
> gas used: 284908
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00569816 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00569816 ETH
2_deploy_contracts.js
=====================
Deploying 'Adoption'
.............................
.............................
在ganache中,也可以看到当前区块发生变化,切当前transaciton也多了
编辑测试
- 在test文件夹下新增 TestAdoption.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.8.0;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";
contract TestAdoption {
Adoption adoption = Adoption(DeployedAddresses.Adoption());
uint expectedPetId = 8;
address expectedAdopter = address(this);
function testUserCanAdoptPet() public {
uint returnedId = adoption.adopt(expectedPetId);
Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
}
function testGetAdopterAddressByPetId() public {
address adopter = adoption.adopters(expectedPetId);
Assert.equal(adopter, expectedAdopter, "Owner of teh expected pet should be this contract");
}
function testGetAdopterAddressByPetIdInArray() public {
address[16] memory adopters = adoption.getAdopters();
Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract");
}
}
输出内容
PS E:\study\truffle\pet-shop-box> truffle test
Using network 'development'.
Compiling your contracts...
===========================
> Compiling .\contracts\Adoption.sol
> Compiling .\contracts\Migrations.sol
> Compiling .\test\TestAdoption.sol
> Compiling truffle\Assert.sol
> Compiling truffle\AssertAddress.sol
> Compiling truffle\AssertAddressArray.sol
> Compiling truffle\AssertBalance.sol
> Compiling truffle\AssertBool.sol
> Compiling truffle\AssertBytes32.sol
> Compiling truffle\AssertBytes32Array.sol
> Compiling truffle\AssertGeneral.sol
> Compiling truffle\AssertInt.sol
> Compiling truffle\AssertIntArray.sol
> Compiling truffle\AssertString.sol
> Compiling truffle\AssertUint.sol
> Compiling truffle\AssertUintArray.sol
> Compiling truffle\DeployedAddresses.sol
> Artifacts written to C:\Users\simon\AppData\Local\Temp\test--27844-JGhn0zeX68hk
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
TestAdoption
√ testUserCanAdoptPet (596ms)
√ testGetAdopterAddressByPetId (818ms)
√ testGetAdopterAddressByPetIdInArray (966ms)
3 passing (20s)
编写用户接口与合约交互
打开项目下 src/app.js
- initWeb3 内容如下
initWeb3: async function () {
if (window.ethereum) {
// 判断是否为dapp浏览器或者由新版本的metaMask已安装到浏览器插件中
App.web3Provider = window.ethereum;
console.log("ethereum window")
try {
await window.ethereum.request({ method: "eth_requestAccounts" });
} catch (error) {
console.log("User denied account accesss")
}
} else if (window.web3) {
// web是否已经安装被安装
console.log("web3 window")
App.web3Provider = window.web3.currentProvider;
} else {
// 本地开发环境提供
console.log("local window")
App.web3Provider = new Web3.providers.HttpProvider("http://localhost:9545");
}
web3 = new Web3(App.web3Provider);
return App.initContract();
}
- 实例化合约
initContract: function () {
$.getJSON("Adoption.json", function (data) {
//获取必要的合约文件,并且 实例化它
var AdoptionArtifact = data;
App.contracts.Adoption = TruffleContract(AdoptionArtifact);
// 为合约提供provider
App.contracts.Adoption.setProvider(App.web3Provider);
return App.markAdopted();
})
return App.bindEvents();
}
- 更新前端UI
markAdopted: function () {
console.log("markAdopted ...")
var adoptionInstance;
App.contracts.Adoption.deployed().then(function (instance) {
adoptionInstance = instance;
console.log("markAdopted ...", adoptionInstance);
return adoptionInstance.getAdopters.call();
}).then(function (adopters) {
console.log("markAdopted ... adopters ", 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.error("markAdopted ...", err.message);
});
}
- Handle Function
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;
return adoptionInstance.adopt(petId, { from: account});
}).then(function(result) {
console.log(result);
return App.markAdopted();
}).catch(function(err) {
console.log(err.message);
});
});
}
运行
npm install
npm run dev
打开浏览器,服务会调用metamask插件,选择一个账户即可
点击 按钮 adopt 会触发 handleAdopt函数。web3 会调用合约中adopt,发起transaction,然后调用 markAdopted函数标记宠物已被收养。
打完收工。