实战:构建一个 Web3 应用
端到端实战:写一个去中心化图书馆合约,用 TronIDE 部署到 Nile 测试网,再把 TronLink 驱动的前端接到合约。
前置阅读
本文从空白目录起步,最终交付一个可运行的原型应用。示例是一个去中心化图书租赁平台:书主上架书籍并按天定价,借阅方支付 TRX 换取租借时长,合约把每次租借记录到链上。
整条主线由三个环节构成:
- 通读图书馆合约的 Solidity 实现
- 通过 TronIDE 将合约部署到 Nile 测试网
- 借助 TronLink 把 Vue 前端接到已部署的合约
准备工作
Node.js v10+
# node -v
v10.24.1TronLink
安装 TronLink Chrome 扩展 并新建或导入账户。DApp 通过 TronLink 向页面注入 TronWeb,并由 TronLink 负责交易签名。
最终成品
一个支持三种用户操作的去中心化图书馆:
- 浏览 —— 列出所有可借书籍
- 上架 —— 上架一本新书
- 借阅 —— 支付 TRX 借走一本书一段时间
克隆完整工程:
git clone https://github.com/TRON-Developer-Hub/decentralized-library
cd decentralized-library
npm install仓库内包含 Solidity 合约和 Vue 前端。接下来按模块拆解协作关系;完整代码以仓库为准。
合约速览
合约源码在上述仓库里。本节梳理链上数据结构与入口函数,便于对照后续前端代码;完整实现、NatSpec 注释、修饰符定义见 Solidity 源文件。
数据结构
合约状态由两个结构体承载:
| 结构体 | 用途 | 关键字段 |
|---|---|---|
Book | 一本上架书籍 | name、description、valid(借出时为 false)、price(每天 TRX)、owner |
Tracking | 一条租借记录 | bookId、startTime、endTime、borrower |
两者都以自增 ID 为键:
uint256 public bookId;
uint256 public trackingId;
mapping(uint256 => Book) public books;
mapping(uint256 => Tracking) public trackings;公开入口函数
| 函数 | 职责 | 触发事件 |
|---|---|---|
addBook(name, description, price) | 上架新书,设 owner = msg.sender,valid = true | NewBook(bookId) |
borrowBook(bookId, startTime, endTime) payable | 收取 price × 天数,向书主转 TRX,写入 Tracking,把 valid 置为 false | NewRental(bookId, trackingId) |
deleteBook(bookId) | 下架;仅书主或合约 owner 可调 | DeleteBook(bookId) |
两个辅助函数为 internal,不可通过 ABI 直接调用:_sendTRX(向书主转账 TRX)与 _createTracking(写入租借记录并把 valid 置为 false)。
部署与测试
把 Solidity 合约部署到 TRON 的工具有多种——TronBox(Truffle 风格的 CLI)、TronWeb 程序化部署、面向 JVM 技术栈的 Trident 等。本文选用 TronIDE——一个无需本地工具链即可编译、部署的浏览器 IDE——目的是把注意力放在 DApp 接入而非工具配置上。
部署目标优先选 Nile 测试网——TRON 公共测试网之一,适合上主网前先验证合约。各网络的差异见 TRON 网络。
领取测试 TRX 覆盖能量成本
合约部署会消耗能量。能量可以来自质押资源;不足时,网络会燃烧 TRX 支付资源成本。前往领取测试网 Token页面领取测试 TRX,每个地址在每个水龙头每天可领一次。
让 TronIDE 接入 TronLink
TronIDE 会自动识别已登录的 TronLink 会话。务必确认 TronLink 当前网络与部署目标一致(本文为 Nile):
启用 TronIDE 模块
首次使用时,到 TronIDE 插件管理器启用 Solidity Compiler 与 DEPLOYMENT 两个模块:
启用后左侧栏出现两个模块:
编译
新建 Library.sol,粘贴仓库内完整合约:
在编译器版本下拉中选择 0.8.0+commit.7c2e641,执行编译。
部署
把 fee_limit 设置为 1000000000(单位 sun,即 1,000 TRX)。它限定本次交易可使用的能量上限,覆盖账户可用的质押能量和可通过燃烧 TRX 换算出的能量,不是简单的“最多燃烧多少 TRX”。设置策略见 fee_limit 与能量费用。
点击 Deploy,TronLink 弹出确认窗,对部署交易签名:
部署上链后,TronIDE 下方面板渲染合约的可调用方法,可借此核对 ABI 是否正确加载:
复制部署后的合约地址,前端会用到。
接入前端
把上一步的合约地址填进 utils.js 的 libraryContractAddress 变量。
TronLink 集成
TronLink 把 TronWeb 对象注入页面,使 DApp 获得一条具备签名能力的 TRON 网络连接。下面把它封装成可复用的合约句柄。
在 dapp-ui/plugins/utils.js 中加入:
export async function setLibraryContract() {
bookRentContract = await
window.tronWeb.contract().at(libraryContractAddress);
}在页面挂载阶段初始化句柄(index.vue),随后加载书单:
async mounted() {
// 初始化合约对象
await setLibraryContract();
// 拉取全部书籍
const books = await fetchAllBooks();
this.posts = books;
}三个用户操作对应合约里的三个公开入口函数。
上架书籍
上架表单(bookForm.vue)收集书名、简介与每日价格。提交时把 TRX 转换成 sun,再调用合约:
postAd() {
// 价格从 TRX 转 sun
postBookInfo(this.title, this.description, tronWeb.toSun(this.price));
}dapp-ui/plugins/utils.js 的 postBookInfo() 调用合约 addBook:
const result = await bookRentContract.addBook(name, description, price).send({
feeLimit: 100_000_000,
callValue: 0,
shouldPollResponse: true
});浏览可借书籍
fetchAllBooks() 从 0 迭代到当前计数器值,逐条读取条目。被删除的条目会留下空槽,通过过滤 book.name 跳过:
const books = [];
const bookId = await bookRentContract.bookId().call();
// 从 0 迭代到 bookId
for (let i = 0; i < bookId; i++) {
const book = await bookRentContract.books(i).call();
if (book.name != "") { // 过滤掉已删除的书籍
books.push({
id: i,
name: book.name,
description: book.description,
price: tronWeb.fromSun(book.price)
});
}
}
return books;在 index.vue 调用 fetchAllBooks() 完成首页渲染。
借阅
详情弹窗(detailsModal.vue)收集借阅时长。客户端计算总费用,再调用合约:
// 起始日
const startDay = this.getDayOfYear(this.startDate);
// 结束日
const endDay = this.getDayOfYear(this.endDate);
// 总价
const totalPrice = tronWeb.toSun(this.propData.price) * (endDay - startDay);
// 调用合约
borrowBook(this.propData.id, startDay, endDay, totalPrice);dapp-ui/plugins/utils.js 的 borrowBook() 把租金作为 callValue 发出去:
const result = await bookRentContract.borrowBook(spaceId, checkInDate, checkOutDate).send({
feeLimit: 100_000_000,
callValue: totalPrice,
shouldPollResponse: true
});DApp 的完整代码到此为止。
运行
登录 TronLink,启动开发服务器:
npm run dev浏览器打开 http://localhost:3000:
点右上角 Rent Your Books 打开上架表单,填好书名、简介和每日价格:
提交,DApp 调用合约 addBook,TronLink 拦截交易并提示签名:
上链后,新书出现在书单中:
点任意书籍的 View 查看详情并选择借阅时长。Lent Now 触发 borrowBook——TronLink 再次提示签名以广播租借交易:
相关资源
- 监听合约事件 —— 订阅本合约抛出的
NewBook、NewRental、DeleteBook事件 - 发送第一笔交易 —— 如果觉得本文跨度大,从这里入门更平缓
- TRC-721 发行 —— 从自己控制的合约发行 Token
- TronLink 集成 —— TronLink provider 事件、多账户处理与断连流程
fee_limit与能量费用 —— 合理配置fee_limit,避免部署或调用时能量不足
Updated 5 days ago