一个简单的Token银行DApp
在上一篇文章中,我们发行了自己的代币 MFT。
这里再创建一个存储MFT币的银行,合约代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract DBank {
// 存储账户余额
mapping(address => uint) private balance;
// 不可变变量
address private immutable token;
constructor(address _token) {
token = _token;
}
// 注意:所有amount应该是用户(或前端应用)直接向合约提供的最小单位数量(wei,或带10^18小数位的单位)
// 单位转换在应用层进行
modifier requireBalance(uint amount) {
require(amount <= balance[msg.sender], "Your balance is not enough.");
_;
}
// 查询余额
function myBalance() public view returns (uint) {
return balance[msg.sender];
}
// 存款 -----> 将个人账户的代币转移到 DBank 合约账户
function deposit(uint amount) public {
// 在代币合约中,将个人账户的 token 转移到 DBank 账户(合约地址)
require(IERC20(token).transferFrom(msg.sender, address(this), amount), "deposit failed");
// 记录
balance[msg.sender] += amount;
}
// 取款 -----> 将 DBank 合约账户的代币转移到个人账户
function withdraw(uint amount) external requireBalance(amount) {
// 在代币合约中,将 DBank 合约账户的 token 转移到个人账户中
// require(IERC20(token).transfer(msg.sender, amount), "withdraw failed");
SafeERC20.safeTransfer(IERC20(token), msg.sender, amount);
// 记录
balance[msg.sender] -= amount;
}
// 转账 -----> 银行内的转账
function bankTransfer(address to, uint amount) public requireBalance(amount) {
// 直接记录
balance[msg.sender] -= amount;
balance[to] += amount;
}
}
然后部署到测试网上,部署的过程不再赘述,这里部署后的合约地址是 0xcf33936D93CFda55e0e0B041B2C0a93817E2fdB1
二、使用react创建前端
这里我在VSCode中使用 Gemini AI 直接生成了一个页面,如下:

当然这还只是一个静态页面。我们需要使用一些 js 库去连接以太坊,与区块链进行交互。
常用的库比如ethers.js 、web3.js、viem.sh等,在Dapp的开发中使用这些库的API功能大部分都是相同的,你可以选择其中的一个即可。相对而言,更推荐 ethers.js、viem.sh,接口也更简洁。
这里我们选择 ethers.js 与区块链进行交互。
1、安装
npm install --save ethers
2、连接钱包,并操作合约
import {ethers} from 'ethers';
import dbankAbi from './ABI.json';
function App() {
const [balance, setBalance] = useState(0);
const [account, setAccount] = useState(null);
const [bankContract, setBankContract] = useState(0);
const [depositAmount, setDepositAmount] = useState('');
const [withdrawAmount, setWithdrawAmount] = useState('');
const [transferAddress, setTransferAddress] = useState('');
const [transferAmount, setTransferAmount] = useState('');
const connectWallet = async() => {
try {
// 1、检查浏览器是否安装了以太坊钱包(如MetaMask)
if (!window.ethereum) {
alert('请安装MetaMask等以太坊钱包插件!');
return;
}
// 2、请求连接钱包,获取账户
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const accounts = await provider.send("eth_requestAccounts", []);
setAccount(accounts[0]);
// 3、访问合约
const dbankAddress = "0xcf33936D93CFda55e0e0B041B2C0a93817E2fdB1";
const theBankContract = new ethers.Contract(dbankAddress, dbankAbi, signer);
setBankContract(theBankContract);
// 首次调用合约的myBalance方法,显示余额
const firstBalance = await theBankContract.myBalance();
setBalance(ethers.formatUnits(firstBalance, 18));
} catch (error) {
console.error("连接钱包失败:", error);
alert("连接钱包失败!");
}
}
const getMyBalance = async() => {
if (!bankContract) {
console.log("银行合约未连接!");
return;
}
try {
// 调用合约的myBalance方法
const myBalance = await bankContract.myBalance();
setBalance(ethers.formatUnits(myBalance, 18));
} catch (error) {
console.error("获取余额失败:", error);
alert("获取余额失败!");
}
}
const deposit = async() => {
if (!bankContract || !depositAmount) {
alert("请输入存款金额!");
return;
}
try {
const tx = await bankContract.deposit(ethers.parseUnits(depositAmount, 18));
await tx.wait(); // 等待交易被打包
alert("存款成功!");
setDepositAmount(''); // 清空输入框
getMyBalance(); // 更新余额
} catch (error) {
console.error("存款失败:", error);
alert("存款失败!");
}
}
const withdraw = async() => {
if (!bankContract || !withdrawAmount) {
alert("请输入取款金额!");
return;
}
try {
const tx = await bankContract.withdraw(ethers.parseUnits(withdrawAmount, 18));
await tx.wait(); // 等待交易被打包
alert("取款成功!");
setWithdrawAmount(''); // 清空输入框
getMyBalance(); // 更新余额
} catch (error) {
console.error("取款失败:", error);
alert("取款失败!");
}
}
const bankTransfer = async() => {
if (!bankContract || !transferAmount) {
alert("请输入转账金额!");
return;
}
try {
const tx = await bankContract.bankTransfer(transferAddress,ethers.parseUnits(transferAmount, 18));
await tx.wait(); // 等待交易被打包
alert("转账成功!");
setTransferAmount(''); // 清空输入框
getMyBalance(); // 更新余额
} catch (error) {
console.error("转账失败:", error);
alert("转账失败!");
}
}
}
优化后最终页面如下:

注意:请事先调用 MyFirstToken (MFT) 合约的approve接口,授权批准 DBank 合约账户可以从你的账户中多次提取,最高可达 25000 MFT币。



浙公网安备 33010602011771号