教程与示例:构建一个简单的 DeFi 借贷 DApp
本教程将指导你在 JuChain 上开发一个简单的去中心化金融(DeFi)借贷 DApp。用户可以存入 JU(JuChain 的原生代币)作为抵押,借出一定比例的 JU,偿还借款后取回抵押品。我们将使用 Solidity 编写智能合约,利用 JuChain 的 EVM 兼容性进行部署,并通过前端与用户交互。
此示例展示了 JuChain 的 秒级交易确认 和 极低成本 优势,非常适合 DeFi 应用场景。
教程:简单 DeFi 借贷 DApp
前提条件
开发环境
Node.js:v16 或更高版本
Truffle Suite:通过
npm install -g truffle
安装MetaMask:浏览器扩展,配置为 JuChain 测试网
基础知识:熟悉 Solidity 和 Web3.js
JuChain 测试网
测试网 RPC:
https://testnet-rpc.juchain.org
网络 ID:
202599
(假设)测试 JU:通过 JuChain 测试网水龙头获取
步骤 1:设置项目
创建项目目录并初始化 Truffle:
mkdir JuChainLending cd JuChainLending truffle init
安装依赖:
npm install @openzeppelin/contracts web3
步骤 2:编写借贷智能合约
在 contracts/
文件夹中创建 LendingPool.sol
:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract LendingPool is ReentrancyGuard {
mapping(address => uint256) public collateral; // 用户抵押的 JU 数量
mapping(address => uint256) public borrowed; // 用户借出的 JU 数量
uint256 public constant LTV = 50; // 贷款价值比(Loan-to-Value),50%
uint256 public constant INTEREST_RATE = 5; // 年化利率 5%(简化为每次还款收取)
event Deposited(address indexed user, uint256 amount);
event Borrowed(address indexed user, uint256 amount);
event Repaid(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
constructor() {
// 无需参数,JU 是原生代币
}
// 存入 JU 作为抵押
function depositCollateral() external payable nonReentrant {
require(msg.value > 0, "Amount must be greater than 0");
collateral[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
// 借出 JU,基于抵押的 50% LTV
function borrow(uint256 amount) external nonReentrant {
require(amount > 0, "Amount must be greater than 0");
uint256 maxBorrow = (collateral[msg.sender] * LTV) / 100;
require(borrowed[msg.sender] + amount <= maxBorrow, "Exceeds max borrowable amount");
borrowed[msg.sender] += amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
emit Borrowed(msg.sender, amount);
}
// 偿还借款(含利息)
function repay(uint256 amount) external payable nonReentrant {
require(amount > 0, "Amount must be greater than 0");
require(borrowed[msg.sender] >= amount, "Repay exceeds borrowed amount");
uint256 interest = (amount * INTEREST_RATE) / 100;
uint256 totalRepay = amount + interest;
require(msg.value >= totalRepay, "Insufficient payment");
borrowed[msg.sender] -= amount;
emit Repaid(msg.sender, amount);
// 退回多余的 JU
if (msg.value > totalRepay) {
(bool success, ) = msg.sender.call{value: msg.value - totalRepay}("");
require(success, "Refund failed");
}
}
// 提取抵押(需先还清借款)
function withdrawCollateral(uint256 amount) external nonReentrant {
require(amount > 0, "Amount must be greater than 0");
require(collateral[msg.sender] >= amount, "Insufficient collateral");
require(borrowed[msg.sender] == 0, "Must repay all loans first");
collateral[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
emit Withdrawn(msg.sender, amount);
}
// 查看用户状态
function getUserStatus(address user) external view returns (uint256, uint256, uint256) {
uint256 maxBorrow = (collateral[user] * LTV) / 100;
return (collateral[user], borrowed[user], maxBorrow);
}
}
功能说明
depositCollateral()
:用户发送 JU(原生代币)作为抵押。borrow(uint256 amount)
:根据 50% LTV 从合约借出 JU。repay(uint256 amount)
:偿还借款并支付 5% 利息,多余的 JU 会退回。withdrawCollateral(uint256 amount)
:还清借款后提取抵押的 JU。getUserStatus(address user)
:查看用户的抵押、借款和最大可借额度。安全性:使用
ReentrancyGuard
防止重入攻击。
注意
JU 是 JuChain 的原生代币,不依赖 ERC20 标准,直接通过 msg.value
和 call
处理。
步骤 3:配置 Truffle
编辑 truffle-config.js
,连接 JuChain 测试网:
const HDWalletProvider = require("@truffle/hdwallet-provider");
const mnemonic = "你的 MetaMask 助记词"; // 替换为你的助记词
module.exports = {
networks: {
juchain_testnet: {
provider: () => new HDWalletProvider(mnemonic, "https://testnet-rpc.juchain.org"),
network_id: 202599,
gas: 5500000,
gasPrice: 1000000000, // 1 gwei
},
},
compilers: {
solc: {
version: "0.8.0",
},
},
};
步骤 4:部署合约
创建部署脚本: 在
migrations/
文件夹中添加2_deploy_lending.js
:const LendingPool = artifacts.require("LendingPool"); module.exports = function (deployer) { deployer.deploy(LendingPool); };
部署到 JuChain 测试网:
truffle migrate --network juchain_testnet
记录部署后的
LendingPool
合约地址。
步骤 5:测试合约
使用 Truffle 控制台验证功能:
进入控制台:
truffle console --network juchain_testnet
测试代码:
let lending = await LendingPool.deployed(); let accounts = await web3.eth.getAccounts(); // 存入 100 JU 作为抵押 await lending.depositCollateral({ from: accounts[0], value: web3.utils.toWei("100") }); console.log((await lending.collateral(accounts[0])).toString()); // 输出 "100000000000000000000" // 借出 50 JU await lending.borrow(web3.utils.toWei("50"), { from: accounts[0] }); console.log((await lending.borrowed(accounts[0])).toString()); // 输出 "50000000000000000000" // 查看用户状态 let status = await lending.getUserStatus(accounts[0]); console.log(status.map(x => web3.utils.fromWei(x))); // 输出 [100, 50, 50] // 偿还 50 JU(含 5% 利息,共 52.5 JU) await lending.repay(web3.utils.toWei("50"), { from: accounts[0], value: web3.utils.toWei("52.5") }); console.log((await lending.borrowed(accounts[0])).toString()); // 输出 "0" // 提取 100 JU await lending.withdrawCollateral(web3.utils.toWei("100"), { from: accounts[0] }); console.log((await lending.collateral(accounts[0])).toString()); // 输出 "0"
步骤 6:创建前端界面
在项目根目录创建 index.html
:
<!DOCTYPE html>
<html>
<head>
<title>JuChain Lending dApp</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; margin-top: 50px; }
button { padding: 10px 20px; margin: 5px; }
input { padding: 5px; margin: 5px; }
</style>
</head>
<body>
<h1>JuChain Lending Pool</h1>
<p>Collateral: <span id="collateral">0</span> JU</p>
<p>Borrowed: <span id="borrowed">0</span> JU</p>
<p>Max Borrowable: <span id="maxBorrow">0</span> JU</p>
<input id="amount" type="number" placeholder="Amount in JU" />
<br />
<button onclick="deposit()">Deposit Collateral</button>
<button onclick="borrow()">Borrow JU</button>
<button onclick="repay()">Repay Loan</button>
<button onclick="withdraw()">Withdraw Collateral</button>
<script src="https://cdn.jsdelivr.net/npm/web3@1.5.3/dist/web3.min.js"></script>
<script>
const contractAddress = "你的 LendingPool 合约地址"; // 替换为实际地址
const abi = [ /* 从 build/contracts/LendingPool.json 获取 ABI */ ]; // 替换为完整 ABI
let web3, lending;
async function init() {
if (window.ethereum) {
web3 = new Web3(window.ethereum);
await window.ethereum.enable();
lending = new web3.eth.Contract(abi, contractAddress);
updateStatus();
} else {
alert("Please install MetaMask!");
}
}
async function updateStatus() {
const accounts = await web3.eth.getAccounts();
const status = await lending.methods.getUserStatus(accounts[0]).call();
document.getElementById("collateral").innerText = web3.utils.fromWei(status[0]);
document.getElementById("borrowed").innerText = web3.utils.fromWei(status[1]);
document.getElementById("maxBorrow").innerText = web3.utils.fromWei(status[2]);
}
async function deposit() {
const accounts = await web3.eth.getAccounts();
const amount = web3.utils.toWei(document.getElementById("amount").value);
await lending.methods.depositCollateral().send({ from: accounts[0], value: amount });
updateStatus();
}
async function borrow() {
const accounts = await web3.eth.getAccounts();
const amount = web3.utils.toWei(document.getElementById("amount").value);
await lending.methods.borrow(amount).send({ from: accounts[0] });
updateStatus();
}
async function repay() {
const accounts = await web3.eth.getAccounts();
const amount = web3.utils.toWei(document.getElementById("amount").value);
const interest = (BigInt(amount) * 5n) / 100n;
const total = (BigInt(amount) + interest).toString();
await lending.methods.repay(amount).send({ from: accounts[0], value: total });
updateStatus();
}
async function withdraw() {
const accounts = await web3.eth.getAccounts();
const amount = web3.utils.toWei(document.getElementById("amount").value);
await lending.methods.withdrawCollateral(amount).send({ from: accounts[0] });
updateStatus();
}
init();
</script>
</body>
</html>
配置说明
ABI:从
build/contracts/LendingPool.json
中复制完整的 ABI 替换到abi
变量。合约地址:将
contractAddress
替换为步骤 4 中部署的地址。运行:使用本地服务器(如
npx http-server
)打开index.html
,通过 MetaMask 连接 JuChain 测试网。
步骤 7:运行和测试
在 MetaMask 中切换到 JuChain 测试网,并确保有足够的测试 JU。
打开浏览器中的
index.html
,依次测试:存入抵押:输入 100 JU,点击 "Deposit Collateral"。
借出 JU:输入 50 JU,点击 "Borrow JU"。
偿还借款:输入 50 JU,点击 "Repay Loan"(自动包含 5% 利息,共 52.5 JU)。
提取抵押:输入 100 JU,点击 "Withdraw Collateral"。
JuChain 的秒级确认将确保交易快速完成,交易成本极低(通常低于 0.001 美元)。
成果
你已成功构建并部署了一个简单的 DeFi 借贷 dApp:
用户可以存入 JU 作为抵押。
根据 50% LTV 借出 JU。
偿还借款并支付 5% 利息后取回抵押品。
前端界面直观显示用户的抵押、借款和可借额度。
此 dApp 展示了 JuChain 的高性能金融基础设施,适合扩展到更复杂的 DeFi 应用(如多资产支持或动态利率)。
注意事项
安全性:
实际生产中需添加清算机制(防止抵押价值不足)并进行代码审计。
当前合约未处理极端情况(如 JU 余额不足)。
测试网:
确保账户中有足够的测试 JU 用于交互。
扩展性:
可添加时间相关的利息计算或支持其他资产。
Last updated