You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.17 20:15:02