我正在使用 Solidity 和 Hardhat 来创建智能合约。版本 6.6.2。这是我的智能合约的样子:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract CreatorRegistry {
struct Creator {
address walletAddress;
string name;
string b64Image;
}
Creator[] public creators;
mapping(address => bool) public isCreatorRegistered;
function signup(address _walletAddress, string memory _name, string memory _b64Image) public {
require(!isCreatorRegistered[_walletAddress], "Address is already registered");
Creator memory newCreator;
newCreator.walletAddress = _walletAddress;
newCreator.name = _name;
newCreator.b64Image = _b64Image;
creators.push(newCreator);
isCreatorRegistered[_walletAddress] = true;
}
function getCreator(uint256 _index) public view returns (
address walletAddress,
string memory name,
string memory b64Image
) {
require(_index < creators.length, "Invalid index");
Creator storage creator = creators[_index];
walletAddress = creator.walletAddress;
name = creator.name;
b64Image = creator.b64Image;
}
function getCreatorCount() public view returns (uint256) {
return creators.length;
}
}
这是我尝试调用合约的方式:
// javascript frontend
const newUser =
(await registrationContract?.getCreatorCount?.()) ??
'Oops we lost';
console.log('New User = ', newUser);
但是失败并出现错误:
Error: could not decode result data (value="0x", info={ "method": "getCreatorCount", "signature": "getCreatorCount()" }, code=BAD_DATA, version=6.6.2)
有人可以帮助我了解这里出了什么问题吗?
从错误来看,它似乎无法解码 javascript 端的数据,或者这是 Solidity 端的问题?我该如何解决这个问题?
编辑1:
正如 @Yilmaz 在评论中提到的:
?.
是 javascript 中的可选链接运算符,可确保在属性未定义时不会使程序崩溃。不过,为了确保万无一失,我也这样做了:
const newUserCount =
(await registrationContract.getCreatorCount()) ??
'Oops we lost';
也失败了。
编辑2 Github 论坛:有一个 github 论坛表明由于网络不同,可能会出现此问题:https://github.com/web3/web3.js/issues/1629
所以我确保我指的是本地主机网络和正确的钱包地址。即使在配置之后,我仍然收到相同的错误:
这就是我的注册合同的样子:
'use client';
import { Contract, InterfaceAbi, ethers } from 'ethers';
import Web3Modal from 'web3modal';
import { ContractRunner } from 'ethers';
import {
contentCreatorABI,
creatorRegistryABI,
contentCreatorAddress,
creatorRegistryAddress,
} from './contractClient';
import { Web3Window } from './web3SignIn';
const getSmartContract = (
address: string,
abi: InterfaceAbi,
provider: ContractRunner
): Contract => new ethers.Contract(address, abi, provider);
const getContracts = async (): Promise<{
error: boolean;
message?: string;
data?: { creatorContract?: Contract; registrationContract: Contract };
}> => {
const { ethereum } = window as Web3Window;
if (!ethereum)
return {
error: true,
message: 'Please install metamask extension and try refreshing the page',
};
try {
const provider = new ethers.BrowserProvider(ethereum);
const signer = await provider.getSigner();
const creatorContract = getSmartContract(
contentCreatorAddress,
contentCreatorABI,
signer
);
const registrationContract = getSmartContract(
creatorRegistryAddress,
creatorRegistryABI,
signer
);
return { error: false, data: { registrationContract } };
} catch (err) {
return { error: true, message: (err as { message: string }).message };
}
};
export { getContracts };
我遇到了类似的情况,并在将智能合约部署到本地网络后设法解决了这个问题:npx Hardhat run scripts/deploy.ts --network localhost
您可以使用 Hardhat 配置中的任何网络作为目标: npx Hardhat run --network < your-network >scripts/deploy.js
对我来说,错误出现是因为我在
uint8
中得到了solidity的响应,这相当于javascript中的long,并且需要正确解密。
正确解密后,问题就解决了。
这是最终代码:
在客户端:
'use client';
import { Contract, InterfaceAbi, ethers } from 'ethers';
import { ContractRunner } from 'ethers';
import {
contentCreatorABI,
creatorRegistryABI,
contentCreatorAddress,
creatorRegistryAddress,
} from './contractClient';
import { Web3Window } from './web3SignIn';
const getSmartContract = (
address: string,
abi: InterfaceAbi,
provider: ContractRunner
): Contract => new ethers.Contract(address, abi, provider);
const getContracts = async (): Promise<{
error: boolean;
message?: string;
data?: { creatorContract: Contract; registrationContract: Contract };
}> => {
const { ethereum } = window as Web3Window;
if (!ethereum)
return {
error: true,
message: 'Please install metamask extension and try refreshing the page',
};
try {
const provider = new ethers.BrowserProvider(ethereum);
const signer = await provider.getSigner();
const creatorContract = getSmartContract(
contentCreatorAddress,
contentCreatorABI,
signer
);
const registrationContract = getSmartContract(
creatorRegistryAddress,
creatorRegistryABI,
signer
);
return { error: false, data: { registrationContract, creatorContract } };
} catch (err) {
return { error: true, message: (err as { message: string }).message };
}
};
export { getContracts };
这是我调用它的方法:
'use client';
import { loginWithWallet } from '@@/solidity/web3SignIn';
import useWeb3Store from '@@/store/web3Walltet';
import classes from './MetaMaskLogin.module.css';
import { useEffect, useState } from 'react';
import { getContracts } from '@@/solidity/contracts';
import { useRouter } from 'next/navigation';
import { NormalImage } from '@@/components/NormalImage';
const MetaMaskLogin = () => {
const {
setAccount,
setNameAndImg,
setCreatorContract,
setRegistrationContract,
registrationContract,
account,
} = useWeb3Store();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [firstTime, setFirstTime] = useState(false);
const [uName, setUName] = useState('');
const [imgUrl, setImgUrl] = useState(
'https://www.gravatar.com/avatar/00000000000000000000000000000000'
);
const router = useRouter();
const loginToMetaMask = async () => {
setLoading(true);
loginWithWallet()
.then((account) => {
if (account) {
setAccount(account);
} else
setError(
'Unexpected error occured while logging in. Please refresh the page and retry.'
);
})
.catch((error) => {
setError(`Error occured while logging in: ${error.message}`);
})
.finally(() => setLoading(false));
};
const getUserDetails = async () => {
if (registrationContract)
try {
const newUserCount =
(await registrationContract.getCreatorByAddress(account)) ??
'Oops we lost';
const [_, name, b64Image] = newUserCount;
if (name === 'Not Found') setFirstTime(true);
else {
setFirstTime(false);
setNameAndImg(name, b64Image);
//TODO: Redirect to main page
router.push('/');
}
} catch (err) {
console.log('Error occured with smart contract = ', err);
}
};
useEffect(() => {
if (account) {
setLoading(true);
getContracts()
.then(async ({ error, data, message }) => {
if (!error && data) {
const { creatorContract, registrationContract } = data;
setCreatorContract(creatorContract);
setRegistrationContract(registrationContract);
} else
setError(
`Error occured while fetching smart contracts: ${
message ?? 'Unknown error'
}`
);
})
.catch((error) => setError(error.message))
.finally(() => setLoading(false));
}
}, [account, setCreatorContract, setRegistrationContract]);
useEffect(() => {
getUserDetails();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [registrationContract]);
const registerUser = async () => {
setLoading(true);
if (registrationContract) {
const user = await registrationContract.signup(account, uName, imgUrl);
await user.wait();
setFirstTime(false);
//TODO: Redirect to main page
setLoading(false);
router.push('/');
}
};
return (
<div className={classes.container}>
<NormalImage
className={classes.metamaskLogo}
src='/meta-mask.png'
/>
{loading ? (
<p className={classes.SignInText}>Loading...</p>
) : (
<button
className={'StandardButton'}
onClick={loginToMetaMask}
>
Login to Metamask
</button>
)}
{error != null && <p className={classes.ErrorText}>{error}</p>}
{firstTime && (
<>
<input
type='text'
className={classes.Input}
placeholder='Please Input your Name'
value={uName}
onChange={(e) => setUName(e.target.value)}
/>
<input
type='url'
className={classes.Input}
placeholder='Please Input your Image Avatar URL'
value={imgUrl}
onChange={(e) => setImgUrl(e.target.value)}
/>
<button
className='StandardButton'
onClick={registerUser}
>
Complete Registration
</button>
</>
)}
</div>
);
};
export default MetaMaskLogin;
在服务器端:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract CreatorRegistry {
struct Creator {
address walletAddress;
string name;
string b64Image;
}
Creator[] public creators;
mapping(address => bool) public isCreatorRegistered;
function signup(address _walletAddress, string memory _name, string memory _b64Image) public {
require(!isCreatorRegistered[_walletAddress], "Address is already registered");
Creator memory newCreator;
newCreator.walletAddress = _walletAddress;
newCreator.name = _name;
newCreator.b64Image = _b64Image;
creators.push(newCreator);
isCreatorRegistered[_walletAddress] = true;
}
function getCreator(uint256 _index) public view returns (
address walletAddress,
string memory name,
string memory b64Image
) {
require(_index < creators.length, "Invalid index");
Creator storage creator = creators[_index];
walletAddress = creator.walletAddress;
name = creator.name;
b64Image = creator.b64Image;
}
function getCreatorCount() public view returns (uint256) {
return creators.length;
}
function getCreatorByAddress(address _walletAddress) public view returns (
address walletAddress,
string memory name,
string memory b64Image
) {
for (uint256 i = 0; i < creators.length; i++) {
Creator storage creator = creators[i];
if (creator.walletAddress == _walletAddress) {
walletAddress = creator.walletAddress;
name = creator.name;
b64Image = creator.b64Image;
return (walletAddress, name, b64Image);
}
}
// Creator not found
return(walletAddress, "Not Found", "Not Found");
}
}
我的问题是我没有运行 npm install 并在项目中安装依赖项。
我的问题解决得很简单,当调用一个应该返回所有者地址的函数时,我收到一个错误:
错误:无法解码结果数据(value =“0x”,info = {“method”:“callme”,“signature”:“callme()”},code = BAD_DATA,version = 6.9.0)
function callme() external view returns (address) {
return msg.sender;
}
通过从函数中删除
view
关键字,一切正常,但在这种情况下,solidity 编译器会发出警告。
所以,我的答案是:
function callme() external returns (address) {
return msg.sender;
}