在上一篇文章(https://www.cnblogs.com/songlee/p/19244400)中,我们发行了自己的代币 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币。

