我正在开发一个dapp,用户可以在其中上传照片并将其制作到NFT。我的应用程序使用web3(Alchemy-web3(、Alchemy、Ropsten和Metamask。我还使用Hardhat来部署我与以下命令的合同:
npx hardhat run --network ropsten scripts/deploy.js
首先,我在浏览器(Firefox(中安装了Metamask,并创建了我的钱包。我还用了一个Ropsten水龙头来取一些乙醚。为了部署我的合约,我在hardhat.config.js中使用了我帐户的私钥。问题是,这样只有我的帐户才能使用合约。我的应用程序应该接受多个用户,每个用户都有自己的Metamask钱包或配置,执行自己的交易。
因此,我是否可以部署或更改我的合同,以便任何用户都可以使用它,而不仅仅是部署它的用户?
这是我的合同:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract NFTminter is ERC721, Ownable {
using Counters for Counters.Counter;
mapping (uint256 => string) private _tokenURIs;
mapping(string => uint256) private hashes;
Counters.Counter private _tokenIdCounter;
constructor() ERC721("MyToken", "PcI") {
}
function _baseURI() internal pure override returns (string memory) {
return "ipfs://";
}
function safeMint(address to, string memory metadataURI) public onlyOwner returns (uint256) {
require(hashes[metadataURI] != 1 , "This metadataURI already exists.");
hashes[metadataURI] = 1;
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, metadataURI);
return tokenId;
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
}
这是我的硬盘。config.js:
require("@nomiclabs/hardhat-waffle");
const { MNEMONIC, ALCHEMY_HTTP } = require("./alchemy_secrets.json");
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
module.exports = {
networks: {
ropsten: {
url: ALCHEMY_HTTP,
accounts: ['PRIVATEKEYGOESHERE']
},
},
solidity: "0.8.4",
};
这是调用合同并发送交易的代码:
import Web3 from "web3";
import CONTRACT_ABI from "../ethContractABI";
import { AbiItem } from "web3-utils";
import {
ROPSTEN_CONTRACT_ADDRESS,
NFT_STORAGE_KEY,
ALCHEMY_API_KEY,
} from "../constants";
import { NFTStorage } from "nft.storage";
import { Contract } from "web3-eth-Contract";
import { AlchemyWeb3, createAlchemyWeb3 } from "@alch/alchemy-web3";
declare global {
interface Window {
ethereum: any;
web3: Web3;
}
}
interface postcardReturn {
ipfsLink?: string | undefined;
tokenID?: number | undefined;
errorMessage?: string | undefined;
}
async function convertToNft(
imageToUpload: File,
etherAddress: string,
privateKey: string
): Promise<postcardReturn> {
try {
await CreateWeb3Object();
const web3 = createAlchemyWeb3(ALCHEMY_API_KEY);
const metadata = await GetNFTmetadata(imageToUpload);
const NFTminter = createNftContract(web3, etherAddress);
//CheckIfTokenExists(NFTminter, metadata, etherAddress);
const receipt = await mintToken(
NFTminter,
metadata,
etherAddress,
web3,
privateKey
);
return {
ipfsLink: metadata.data.image.href,
tokenID: receipt!.events!.Transfer.returnValues.tokenId,
};
} catch (error: any) {
return returnError(error);
}
}
async function GetNFTmetadata(imageToUpload: File) {
const client = new NFTStorage({ token: NFT_STORAGE_KEY });
const metadata = await client.store({
name: "From: User",
description: "IMage to be converted to nft",
image: imageToUpload,
});
return metadata;
}
async function CreateWeb3Object() {
if (window.ethereum) {
try {
const enable = window.ethereum.enable();
return;
} catch (error) {
console.log(error);
}
}
}
async function CheckIfTokenExists(
NFTminter: Contract,
metadata: any,
etherAddress: string
) {
const check = await NFTminter.methods
.safeMint(etherAddress, metadata.url)
.estimateGas((error: any, gasAmount: any) => {
if (error) {
console.error(error);
return "An error has occured";
}
});
}
function createNftContract(web3: any, etherAddress: string) {
const NFTminter = new web3.eth.Contract(
CONTRACT_ABI,
ROPSTEN_CONTRACT_ADDRESS
);
return NFTminter;
}
async function mintToken(
NFTminter: Contract,
metadata: any,
etherAddress: string,
web3: AlchemyWeb3,
privateKey: string
) {
const nonce = await web3.eth.getTransactionCount(etherAddress, "latest");
const tx = {
from: etherAddress,
to: ROPSTEN_CONTRACT_ADDRESS,
nonce: nonce,
gas: 2000000,
maxPriorityFeePerGas: 1999999987,
data: NFTminter.methods
.safeMint(etherAddress, metadata.data.image.href)
.encodeABI(),
};
const signedTx = await web3.eth.accounts.signTransaction(tx, privateKey);
web3.eth.sendSignedTransaction(
signedTx.rawTransaction!
).then(console.log)
.catch(console.log);
const transactionReceipt = await web3.eth.sendSignedTransaction(
signedTx.rawTransaction!
);
console.log(transactionReceipt);
return transactionReceipt;
}
function returnError(error: any) {
if (error.message.includes("Internal JSON-RPC error."))
return {
errorMessage: "Internal JSON-RPC error.",
};
return {
errorMessage: error.message,
};
}
export default convertToNft;
privateKey变量由用户在单击上载按钮之前给定。如果我更改我使用的帐户,则会在以下行引发异常:
const signedTx = await web3.eth.accounts.signTransaction(tx, privateKey);
例外情况是来电者不是合同所有者。我认为我所描述的问题就是问题所在,因为我重新部署了我现在使用的Metamask账户的合同,我通常可以发送和签署交易。但是,如果我更改帐户,我会收到一个错误。
您的safeMint函数只有Owner修饰符。这就是为什么只有合同部署人员才能使用它