Dapp对接Solidity合约:Profile.tsx开发及TypeScript前端交互求助
智能合约问题修正
你当前提供的Solidity合约存在两个核心逻辑问题,无法正常存储和读取多用户数据,先修正如下:
pragma solidity ^0.6.0; contract Register { // 定义用户结构体存储所有信息 struct User { string name; string dept; uint phonenumber; string addresshome; string emailid; uint aadhar; string ipfshash1; string ipfshash2; string ipfshash3; string ipfshash4; } // 按钱包地址映射存储不同用户的信息 mapping(address => User) public users; // 更新用户基础信息的方法 function updateUserInfo( string memory _name, string memory _dept, uint _phonenumber, string memory _addresshome, string memory _emailid, uint _aadhar ) public { User storage user = users[msg.sender]; user.name = _name; user.dept = _dept; user.phonenumber = _phonenumber; user.addresshome = _addresshome; user.emailid = _emailid; user.aadhar = _aadhar; } // 证书哈希相关方法 function sendhash1(string memory a) public { users[msg.sender].ipfshash1 = a; } function gethash1() public view returns(string memory a) { return users[msg.sender].ipfshash1; } function sendhash2(string memory b) public { users[msg.sender].ipfshash2 = b; } function gethash2() public view returns(string memory b) { return users[msg.sender].ipfshash2; } function sendhash3(string memory c) public { users[msg.sender].ipfshash3 = c; } function gethash3() public view returns(string memory c) { return users[msg.sender].ipfshash3; } function sendhash4(string memory d) public { users[msg.sender].ipfshash4 = d; } function gethash4() public view returns(string memory d) { return users[msg.sender].ipfshash4; } }
修改后可以按钱包地址区分用户,所有读取方法都会返回当前调用者存在链上的真实数据。
前端实现步骤
1. 登录跳转逻辑调整
你可以在Login组件的父组件中处理onLoggedIn回调,用react-router的路由跳转能力跳转到profile页面,同时把当前登录的钱包地址存入localStorage方便后续使用,示例逻辑如下:
// 父组件示例代码 import { useNavigate } from 'react-router-dom'; import { Login } from './Login'; import { Auth } from '../types'; const App = () => { const navigate = useNavigate(); const handleLoginSuccess = (auth: Auth) => { // 把后端返回的token、钱包地址存入localStorage localStorage.setItem('accessToken', auth.accessToken); localStorage.setItem('publicAddress', auth.publicAddress); // 跳转到profile页面 navigate('/profile'); }; return <Login onLoggedIn={handleLoginSuccess} /> }
2. Profile.tsx完整代码
首先你需要先编译修正后的合约,得到合约ABI数组,同时记录合约部署到链上的实际地址,替换代码中的占位内容即可:
import React, { useState, useEffect } from 'react'; import Web3 from 'web3'; // 替换为你自己编译合约得到的ABI数组 const REGISTER_CONTRACT_ABI = [ // 此处粘贴你编译生成的ABI内容,示例如下: // {"inputs":[],"name":"gethash1","outputs":[{"internalType":"string","name":"a","type":"string"}],"stateMutability":"view","type":"function"}, // {"inputs":[{"internalType":"string","name":"a","type":"string"}],"name":"sendhash1","outputs":[],"stateMutability":"nonpayable","type":"function"}, // ...其他ABI项 ] as const; // 替换为你部署后的合约实际地址 const REGISTER_CONTRACT_ADDRESS = '0x你部署的合约地址'; interface UserInfo { name: string; dept: string; phonenumber: number; addresshome: string; emailid: string; aadhar: number; ipfshash1: string; ipfshash2: string; ipfshash3: string; ipfshash4: string; } export const Profile = () => { const [web3, setWeb3] = useState<Web3 | null>(null); const [userInfo, setUserInfo] = useState<UserInfo | null>(null); const [loading, setLoading] = useState(true); const publicAddress = localStorage.getItem('publicAddress'); // 初始化web3和合约实例 useEffect(() => { const init = async () => { if (!(window as any).ethereum || !publicAddress) { window.alert('请先登录MetaMask'); window.location.href = '/login'; return; } const web3Instance = new Web3((window as any).ethereum); setWeb3(web3Instance); // 初始化合约实例 const contract = new web3Instance.eth.Contract( REGISTER_CONTRACT_ABI as any, REGISTER_CONTRACT_ADDRESS ); try { // 读取用户基础信息 const userData = await contract.methods.users(publicAddress).call({ from: publicAddress }); // 读取证书哈希 const hash1 = await contract.methods.gethash1().call({ from: publicAddress }); const hash2 = await contract.methods.gethash2().call({ from: publicAddress }); const hash3 = await contract.methods.gethash3().call({ from: publicAddress }); const hash4 = await contract.methods.gethash4().call({ from: publicAddress }); setUserInfo({ name: userData.name, dept: userData.dept, phonenumber: Number(userData.phonenumber), addresshome: userData.addresshome, emailid: userData.emailid, aadhar: Number(userData.aadhar), ipfshash1: hash1, ipfshash2: hash2, ipfshash3: hash3, ipfshash4: hash4 }); } catch (err) { console.error('读取链上数据失败', err); window.alert('读取用户信息失败'); } finally { setLoading(false); } }; init(); }, [publicAddress]); if (loading) return <div>加载中...</div>; if (!userInfo) return <div>未查询到用户信息</div>; return ( <div style={{ maxWidth: 800, margin: '0 auto', padding: '20px' }}> <h2>个人信息页</h2> <div style={{ marginBottom: '20px' }}> <p>姓名:{userInfo.name || '未填写'}</p> <p>部门:{userInfo.dept || '未填写'}</p> <p>手机号:{userInfo.phonenumber || '未填写'}</p> <p>住址:{userInfo.addresshome || '未填写'}</p> <p>邮箱:{userInfo.emailid || '未填写'}</p> <p>Aadhar号:{userInfo.aadhar || '未填写'}</p> </div> <h3>证书文件</h3> <div> {userInfo.ipfshash1 && ( <p><a href={`https://ipfs.io/ipfs/${userInfo.ipfshash1}`} target="_blank" rel="noopener noreferrer">证书1</a></p> )} {userInfo.ipfshash2 && ( <p><a href={`https://ipfs.io/ipfs/${userInfo.ipfshash2}`} target="_blank" rel="noopener noreferrer">证书2</a></p> )} {userInfo.ipfshash3 && ( <p><a href={`https://ipfs.io/ipfs/${userInfo.ipfshash3}`} target="_blank" rel="noopener noreferrer">证书3</a></p> )} {userInfo.ipfshash4 && ( <p><a href={`https://ipfs.io/ipfs/${userInfo.ipfshash4}`} target="_blank" rel="noopener noreferrer">证书4</a></p> )} </div> </div> ); };
注意事项
- 所有合约相关的地址、ABI都要替换为你自己部署编译后的实际内容
- 如果需要在前端实现信息修改功能,调用合约的updateUserInfo、sendhash等写方法时,需要传入from参数指定当前钱包地址,MetaMask会弹出签名窗口,用户签名支付gas后才会完成链上数据修改
- IPFS公共网关访问速度可能较慢,可以根据需求替换为自己搭建的私有网关
内容的提问来源于stack exchange,提问作者Padma Shruthi
相关产品推荐
相关产品推荐

