I'm trying to deploy my contract to the goerli testnet however hardhat keeps throwing this error:
Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (reason="execution reverted", method="estimateGas", transaction={"from":"0xb5E8683Aa524069C5714Fc2D8c3e64F78f2862fb","data":"0x6103e86009556... (I shortened it down)","accessList":null}, error={"name":"ProviderError","_stack":"ProviderError: HttpProviderError\n at HttpProvider.request (C:\\Programming\\Personal Projects\\NFT Minting Dapp\\smart-contract\\node_modules\\hardhat\\src\\internal\\core\\providers\\http.ts:78:19)\n at LocalAccountsProvider.request (C:\\Programming\\Personal Projects\\NFT Minting Dapp\\smart-contract\\node_modules\\hardhat\\src\\internal\\core\\providers\\accounts.ts:187:34)\n at processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async EthersProviderWrapper.send (C:\\Programming\\Personal Projects\\NFT Minting Dapp\\smart-contract\\node_modules\\#nomiclabs\\hardhat-ethers\\src\\internal\\ethers-provider-wrapper.ts:13:20)","code":3,"_isProviderError":true,"data":"0xbfc6c337000000000000000000000000fd7bfa171b5b81b79c245456e986db2f32fbfadb"}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.7.2)
at Logger.makeError (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\#ethersproject\logger\src.ts\index.ts:269:28)
at Logger.throwError (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\#ethersproject\logger\src.ts\index.ts:281:20)
at checkError (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\#ethersproject\providers\src.ts\json-rpc-provider.ts:78:20)
at EthersProviderWrapper.<anonymous> (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\#ethersproject\providers\src.ts\json-rpc-provider.ts:642:20)
at step (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\#ethersproject\providers\lib\json-rpc-provider.js:48:23)
at Object.throw (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\#ethersproject\providers\lib\json-rpc-provider.js:29:53)
at rejected (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\#ethersproject\providers\lib\json-rpc-provider.js:21:65)
at processTicksAndRejections (node:internal/process/task_queues:95:5) {
reason: 'execution reverted',
code: 'UNPREDICTABLE_GAS_LIMIT',
method: 'estimateGas',
transaction: {
from: '0xb5E8683Aa524069C5714Fc2D8c3e64F78f2862fb',
data: '0x6103e86009556... (I shortened it down)',
accessList: null
}
Here is my contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/// #title John's ERC721A Contract
/// #author John Pioc (www.johnpioc.com)
/// #notice This contract can be used to mint ERC721A standard NFTs with industry standard functionality - whitelisted addresses, reveals, NFT metadata, etc.
import "erc721a/contracts/ERC721A.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Strings.sol";
import "#openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {DefaultOperatorFilterer} from "./DefaultOperatorFilterer.sol";
error CallerIsNotUser();
error InvalidWhitelistAllocation();
error MintingTooMany();
error InsufficientFunds();
error MintingOverCollectionSize();
error SaleIsClosed();
error MintingOverWhitelistAllocation();
error InvalidProof();
error URIQueryForNonexistentToken();
contract NFT is ERC721A, Ownable, DefaultOperatorFilterer {
using Strings for uint256;
enum SaleState {
CLOSED,
WHITELIST,
PUBLIC
}
uint256 public collectionSize = 1000;
uint256 public publicMintPrice = 0.1 ether;
uint256 public publicMaxMintAmount = 3;
uint256 public whitelistAllocation = 500;
uint256 public whitelistMintPrice = 0.05 ether;
uint256 public whitelistMaxMintAmount = 1;
bytes32 public whitelistMerkleRoot;
string public unrevealedUri = "https://exampleUnrevealedUri.com";
string public baseUri = "https://exampleUri.com/";
bool public isRevealed;
SaleState public saleState;
/// #notice Modifier to verify that caller doesn't come from a contract
modifier callerIsUser() {
if (tx.origin != msg.sender) revert CallerIsNotUser();
_;
}
constructor() ERC721A ("NFT", "NFT") {
isRevealed = false;
saleState = SaleState.CLOSED;
}
/// #notice Function to mint NFTs during the public sale
/// #param _mintAmount Number of NFTs to mint
function publicMint(uint64 _mintAmount) public payable callerIsUser {
if (_numberMinted(msg.sender) - _getAux(msg.sender) + _mintAmount > publicMaxMintAmount) revert MintingTooMany();
if (totalSupply() + _mintAmount > collectionSize) revert MintingOverCollectionSize();
if (saleState != SaleState.PUBLIC) revert SaleIsClosed();
if (msg.value < _mintAmount * publicMintPrice) revert InsufficientFunds();
_safeMint(msg.sender, _mintAmount);
}
/// #notice Function to mint NFTs during the whitelist sale
/// #param _merkleProof Merkle Proof for caller's address
/// #param _mintAmount Number of NFTs to mint
function whitelistMint(bytes32[] calldata _merkleProof, uint64 _mintAmount) public payable callerIsUser {
if (_getAux(msg.sender) + _mintAmount > whitelistMaxMintAmount) revert MintingTooMany();
if (totalSupply() + _mintAmount > whitelistAllocation) revert MintingOverWhitelistAllocation();
if (saleState != SaleState.WHITELIST) revert SaleIsClosed();
if (msg.value < _mintAmount * whitelistMintPrice) revert InsufficientFunds();
bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
if(!(MerkleProof.verify(_merkleProof, whitelistMerkleRoot, leaf))) revert InvalidProof();
_setAux(msg.sender, _getAux(msg.sender) + _mintAmount);
_safeMint(msg.sender, _mintAmount);
}
/// #notice Sets a new collection size
/// #dev Only owner can call this function
/// #param _collectionSize New Collection Size
function setCollectionSize(uint256 _collectionSize) public onlyOwner {
collectionSize = _collectionSize;
}
/// #notice Sets a new public mint price
/// #dev Only owner can call this function
/// #param _publicMintPrice New public mint price
function setPublicMintPrice(uint256 _publicMintPrice) public onlyOwner {
publicMintPrice = _publicMintPrice;
}
/// #notice Sets a new public max mint amount
/// #dev Only owner can call this function
/// #param _publicMaxMintAmount New public max mint amount
function setPublicMaxMintAmount(uint256 _publicMaxMintAmount) public onlyOwner {
publicMaxMintAmount = _publicMaxMintAmount;
}
/// #notice Sets a new whitelist allocation
/// #dev Only owner can call this function. New whitelist allocation cannot be greater than collection size
/// #param _whitelistAllocation New whitelist allocation
function setWhitelistAllocation(uint256 _whitelistAllocation) public onlyOwner {
if (_whitelistAllocation > collectionSize) revert InvalidWhitelistAllocation();
whitelistAllocation = _whitelistAllocation;
}
/// #notice Sets a new whitelist mint price
/// #dev Only owner can call this function
/// #param _whitelistMintPrice New whitelist mint price
function setWhitelistMintPrice(uint256 _whitelistMintPrice) public onlyOwner {
whitelistMintPrice = _whitelistMintPrice;
}
/// #notice Sets a new whitelist max mint amount
/// #dev Only owner can call this function
/// #param _whitelistMaxMintAmount New whitelist max mint amount
function setWhitelistMaxMintAmount(uint256 _whitelistMaxMintAmount) public onlyOwner {
whitelistMaxMintAmount = _whitelistMaxMintAmount;
}
/// #notice Sets a new whitelist merkle root
/// #dev Only owner can call this function
/// #param _whitelistMerkleRoot New whitelist merkle root
function setWhitelistMerkleRoot(bytes32 _whitelistMerkleRoot) public onlyOwner {
whitelistMerkleRoot = _whitelistMerkleRoot;
}
/// #notice Sets a new unrevealed URI
/// #dev Only owner can call this function
/// #param _unrevealedUri New unrevealed URI
function setUnrevealedUri(string memory _unrevealedUri) public onlyOwner {
unrevealedUri = _unrevealedUri;
}
/// #notice Sets a new base URI
/// #dev Only owner can call this function
/// #param _baseUri New base URI
function setBaseUri(string memory _baseUri) public onlyOwner {
baseUri = _baseUri;
}
/// #notice Toggles reveal from false to true, vice versa
/// #dev Only owner can call this function. Starts at false
function toggleRevealed() public onlyOwner {
isRevealed = !isRevealed;
}
/// #notice Sets a new sale state
/// #dev Only owner can call this function. 0 = CLOSED, 1 = WHITELIST, 2 = PUBLIC
/// #param _saleState new sale state
function setSaleState(uint256 _saleState) public onlyOwner {
saleState = SaleState(_saleState);
}
/// #notice Generates and returns the token URI for a given token ID
/// #param _tokenId An NFT's token ID
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
if (!_exists(_tokenId)) revert URIQueryForNonexistentToken();
if (!isRevealed) return unrevealedUri;
return string(abi.encodePacked(baseUri, _tokenId.toString(), ".json"));
}
/// #notice Withdraws all ETH from contract to owner's address
/// #dev Only owner can call this function
function withdraw() public payable onlyOwner {
(bool os,) = payable(owner()).call{value: address(this).balance}("");
require(os);
}
/// #notice All functions below are mandatory add-ons as per the Default Operator Filterer protocol
function transferFrom(address from, address to, uint256 tokenId) public payable override onlyAllowedOperator(from) {
super.transferFrom(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId) public payable override onlyAllowedOperator(from) {
super.safeTransferFrom(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
public
payable
override
onlyAllowedOperator(from)
{
super.safeTransferFrom(from, to, tokenId, data);
}
}
Here is my config file:
import { HardhatUserConfig } from "hardhat/config";
import "#nomicfoundation/hardhat-toolbox";
import "#nomiclabs/hardhat-waffle";
import "dotenv/config";
import "#typechain/hardhat";
const GOERLI_RPC_URL = process.env.GOERLI_RPC_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY;
const config: HardhatUserConfig = {
solidity: {
version: "0.8.17",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
networks: {
goerli: {
url: GOERLI_RPC_URL,
accounts: [PRIVATE_KEY!],
chainId: 5,
allowUnlimitedContractSize: true,
}
},
etherscan: {
apiKey: {
goerli: ETHERSCAN_API_KEY!
}
}
};
export default config;
and here is my deploy script:
// imports
import { ethers, run, network } from 'hardhat';
// async main
async function main() {
const contractFactory = await ethers.getContractFactory("NFT");
const contract = await contractFactory.deploy();
console.log(`Contract deployed to ${contract.address}`);
if (network.config.chainId === 5 && process.env.ETHERSCAN_API_KEY) {
console.log("Waiting for block confirmations...")
await contract.deployTransaction.wait(6)
await verify(contract.address, [])
}
}
// async function verify(contractAddress, args) {
const verify = async (contractAddress: string, args: any[]) => {
console.log("Verifying contract...")
try {
await run("verify:verify", {
address: contractAddress,
constructorArguments: args,
})
} catch (e: any) {
if (e.message.toLowerCase().includes("already verified")) {
console.log("Already Verified!")
} else {
console.log(e)
}
}
}
// main
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
I ran the command:
npx hardhat run scripts/deploy.ts --network goerli
And by running that command, it throws the error. I double checked that the it's grabbing the environment variables and everything. I actually forked all this code from one of my repos where the deploy script worked back then, however now it's not
I've had some similar issues when using hardhat to deploy for both goerli and mainnet. What seemed to solve it for me is manually adding in a gas limit and gas price, here is what your goerli config object should look like, just be aware you'll want to adjust the limit and price to account for current gas prices.
goerli: {
url: GOERLI_RPC_URL,
accounts: [PRIVATE_KEY!],
chainId: 5,
allowUnlimitedContractSize: true,
gas: 5000000, //units of gas you are willing to pay, aka gas limit
gasPrice: 50000000000, //gas is typically in units of gwei, but you must enter it as wei here
}
Reading the hardhat config docs is also worth a look here
Check the eth gas tracker to see up to date gas price estimates
Related
I am practising this tutorial in Remix IDE - https://www.youtube.com/watch?v=_aXumgdpnPU
I saw in the Chainlink documentation that their Randomness VRF code has been changed since the development of the video.
I started replacing the parts and trying to deploy the class via Remix but it gives an error which I am not sure how to fix.
Would you be able to check what I have as code and I'll send a screenshot of the error?
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
import "#chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
import "#chainlink/contracts/src/v0.8/VRFV2WrapperConsumerBase.sol";
contract Lottery is
VRFV2WrapperConsumerBase,
ConfirmedOwner
{
address public owner;
address payable[] public players;
uint public lotteryId;
mapping (uint => address payable) public lotteryHistory;
event RequestSent(uint256 requestId, uint32 numWords);
event RequestFulfilled(
uint256 requestId,
uint256[] randomWords,
uint256 payment
);
struct RequestStatus {
uint256 paid; // amount paid in link
bool fulfilled; // whether the request has been successfully fulfilled
uint256[] randomWords;
}
mapping(uint256 => RequestStatus)
public s_requests; /* requestId --> requestStatus */
// past requests Id.
uint256[] public requestIds;
uint256 public lastRequestId;
// Depends on the number of requested values that you want sent to the
// fulfillRandomWords() function. Test and adjust
// this limit based on the network that you select, the size of the request,
// and the processing of the callback request in the fulfillRandomWords()
// function.
uint32 callbackGasLimit = 100000;
// The default is 3, but you can set this higher.
uint16 requestConfirmations = 3;
// For this example, retrieve 2 random values in one request.
// Cannot exceed VRFV2Wrapper.getConfig().maxNumWords.
uint32 numWords = 2;
// Address LINK - hardcoded for Goerli
address linkAddress = 0x326C977E6efc84E512bB9C30f76E30c160eD06FB;
// address WRAPPER - hardcoded for Goerli
address wrapperAddress = 0x708701a1DfF4f478de54383E49a627eD4852C816;
constructor()
ConfirmedOwner(msg.sender)
VRFV2WrapperConsumerBase(linkAddress, wrapperAddress)
{
owner = msg.sender;
lotteryId = 1;
}
function requestRandomWords()
external
onlyOwner
returns (uint256 requestId)
{
requestId = requestRandomness(
callbackGasLimit,
requestConfirmations,
numWords
);
s_requests[requestId] = RequestStatus({
paid: VRF_V2_WRAPPER.calculateRequestPrice(callbackGasLimit),
randomWords: new uint256[](0),
fulfilled: false
});
requestIds.push(requestId);
lastRequestId = requestId;
emit RequestSent(requestId, numWords);
return requestId;
}
function fulfillRandomWords(
uint256 _requestId,
uint256[] memory _randomWords
) internal override {
require(s_requests[_requestId].paid > 0, "request not found");
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
emit RequestFulfilled(
_requestId,
_randomWords,
s_requests[_requestId].paid
);
payWinner();
}
function getRequestStatus(
uint256 _requestId
)
external
view
returns (uint256 paid, bool fulfilled, uint256[] memory randomWords)
{
require(s_requests[_requestId].paid > 0, "request not found");
RequestStatus memory request = s_requests[_requestId];
return (request.paid, request.fulfilled, request.randomWords);
}
/**
* Allow withdraw of Link tokens from the contract
*/
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(linkAddress);
require(
link.transfer(msg.sender, link.balanceOf(address(this))),
"Unable to transfer"
);
}
function getWinnerByLottery(uint lottery) public view returns (address payable) {
return lotteryHistory[lottery];
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
function getPlayers() public view returns (address payable[] memory) {
return players;
}
function enter() public payable {
require(msg.value > .01 ether);
// address of player entering lottery
players.push(payable(msg.sender));
}
//function getRandomNumber() public view returns (uint) {
//return uint(keccak256(abi.encodePacked(owner, block.timestamp)));
//}
function pickWinner() public onlyowner {
requestRandomWords;
}
function payWinner() public {
uint index = lastRequestId % players.length;
players[index].transfer(address(this).balance);
lotteryHistory[lotteryId] = players[index];
lotteryId++;
// reset the state of the contract
players = new address payable[](0);
}
modifier onlyowner() {
require(msg.sender == owner);
_;
}
}
enter image description here
I'm new to smart contract and Solidity programming and I've been trying to deploy an NFT contract on goerli testnet. The contract deploys without any problem but when I try to call the "createCollectible" function, I get the following error:
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
execution reverted
{
"originalError": {
"code": 3,
"data": "0xf0019fe60000000000000000000000000000000000000000000000000000000000000c6a000000000000000000000000b54644506388a04187d943dbbbd3edbb3ee53094",
"message": "execution reverted"
}
}
Here is my contract:
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.9.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "#chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
import "#chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
/**
* #title The AdvandedCollectible contract
* #notice A contract that mints an NFT
*/
contract AdvancedCollectible is ERC721, VRFConsumerBaseV2 {
VRFCoordinatorV2Interface immutable COORDINATOR;
LinkTokenInterface immutable LINKTOKEN;
uint256 public tokenCounter;
uint64 immutable s_subscriptionId;
// The gas lane to use, which specifies the maximum gas price to bump to.
// For a list of available gas lanes on each network,
// see https://docs.chain.link/docs/vrf-contracts/#configurations
bytes32 immutable s_keyhash;
uint32 immutable s_numWords = 1;
uint16 immutable s_requestConfirmations = 3;
// Depends on the number of requested values that you want sent to the
// fulfillRandomWords() function. Storing each word costs about 20,000 gas,
// so 100,000 is a safe default for this example contract. Test and adjust
// this limit based on the network that you select, the size of the request,
// and the processing of the callback request in the fulfillRandomWords()
// function.
uint32 immutable s_callbackGasLimit = 100000;
uint256 public s_requestId;
uint256 public s_randomWords;
address s_owner;
enum Breed {
PUG,
SHIBA_INU,
ST_BERNARD
}
mapping(uint256 => Breed) public tokenIdToBreed;
mapping(uint256 => address) public requestIdToSender;
event requestCollectible(uint256 indexed requestId, address requester);
event breedAssigned(uint256 tokenId, Breed breed);
constructor(
address vrfCoordinator,
address link,
bytes32 keyhash,
uint64 subscriptionId
) VRFConsumerBaseV2(vrfCoordinator) ERC721("Dogie", "DOG") {
tokenCounter = 0;
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
LINKTOKEN = LinkTokenInterface(link);
s_keyhash = keyhash;
s_owner = msg.sender;
s_subscriptionId = subscriptionId;
}
modifier onlyOwner() {
require(msg.sender == s_owner, "You are not the owner");
_;
}
function createCollectible() public returns (uint256) {
s_requestId = COORDINATOR.requestRandomWords(
s_keyhash,
s_subscriptionId,
s_requestConfirmations,
s_callbackGasLimit,
s_numWords
);
requestIdToSender[s_requestId] = msg.sender;
emit requestCollectible(s_requestId, msg.sender);
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords)
internal
override
{
s_randomWords = randomWords[0];
Breed breed = Breed(s_randomWords % 3);
uint256 newTokenId = tokenCounter;
tokenIdToBreed[newTokenId] = breed;
emit breedAssigned(newTokenId, breed);
address owner = requestIdToSender[requestId];
_safeMint(owner, newTokenId);
tokenCounter++;
}
function setTokenURI(uint256 tokenId, string memory _tokenURI) public {
require(
_isApprovedOrOwner(_msgSender(), tokenId),
"ERC721: Caller is not owner nor approved."
);
setTokenURI(tokenId, _tokenURI);
}
}
Does anybody have an idea what I'm doing wrong here?
So I realized that I'm missing one small yet important step, and that is adding a consumer to my subscription.
Using the Chainlink VRF V2, you need to create a subscription, add funds to that subscription and add the address where your contract is deployed at as a consumer to the subscription. I wasn't doing that last step and that's why my transaction's execution kept reverting.
I am trying to create a test around a contract, but I am having problems understanding how to sign it in a test environment
This is my contract
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.9 <0.9.0;
import "#divergencetech/ethier/contracts/crypto/SignatureChecker.sol";
import "#divergencetech/ethier/contracts/crypto/SignerManager.sol";
import "#divergencetech/ethier/contracts/erc721/BaseTokenURI.sol";
import "#divergencetech/ethier/contracts/erc721/ERC721ACommon.sol";
import "#divergencetech/ethier/contracts/erc721/ERC721Redeemer.sol";
import "#divergencetech/ethier/contracts/sales/FixedPriceSeller.sol";
import "#divergencetech/ethier/contracts/utils/Monotonic.sol";
import "#openzeppelin/contracts/token/common/ERC2981.sol";
import "#openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "#openzeppelin/contracts/utils/structs/EnumerableSet.sol";
interface ITokenURIGenerator {
function tokenURI(uint256 tokenId) external view returns (string memory);
}
// #author divergence.xyz
contract TestBirds is
ERC721ACommon,
BaseTokenURI,
FixedPriceSeller,
ERC2981,
AccessControlEnumerable
{
using EnumerableSet for EnumerableSet.AddressSet;
using ERC721Redeemer for ERC721Redeemer.Claims;
using Monotonic for Monotonic.Increaser;
/**
#notice Role of administrative users allowed to expel a Player from the
mission.
#dev See expelFromMission().
*/
bytes32 public constant EXPULSION_ROLE = keccak256("EXPULSION_ROLE");
constructor(
string memory name,
string memory symbol,
string memory baseTokenURI,
address payable beneficiary,
address payable royaltyReceiver
)
ERC721ACommon(name, symbol)
BaseTokenURI(baseTokenURI)
FixedPriceSeller(
2.5 ether,
Seller.SellerConfig({
totalInventory: 10_000,
lockTotalInventory: true,
maxPerAddress: 0,
maxPerTx: 0,
freeQuota: 125,
lockFreeQuota: false,
reserveFreeQuota: true
}),
beneficiary
)
{
_setDefaultRoyalty(royaltyReceiver, 1000);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/**
#dev Mint tokens purchased via the Seller.
*/
function _handlePurchase(
address to,
uint256 n,
bool
) internal override {
_safeMint(to, n);
}
/**
#dev Record of already-used signatures.
*/
mapping(bytes32 => bool) public usedMessages;
/**
#notice Mint tokens.
*/
function mintPublic(
address to,
bytes32 nonce,
bytes calldata sig
) external payable {
signers.requireValidSignature(
signaturePayload(to, nonce),
sig,
usedMessages
);
_purchase(to, 1);
}
function alreadyMinted(address to, bytes32 nonce)
external
view
returns (bool)
{
return
usedMessages[
SignatureChecker.generateMessage(signaturePayload(to, nonce))
];
}
/**
#dev Constructs the buffer that is hashed for validation with a minting
signature.
*/
function signaturePayload(address to, bytes32 nonce)
internal
pure
returns (bytes memory)
{
return abi.encodePacked(to, nonce);
}
/**
#dev Required override to select the correct baseTokenURI.
*/
function _baseURI()
internal
view
override(BaseTokenURI, ERC721A)
returns (string memory)
{
return BaseTokenURI._baseURI();
}
/**
#notice If set, contract to which tokenURI() calls are proxied.
*/
ITokenURIGenerator public renderingContract;
/**
#notice Sets the optional tokenURI override contract.
*/
function setRenderingContract(ITokenURIGenerator _contract)
external
onlyOwner
{
renderingContract = _contract;
}
/**
#notice If renderingContract is set then returns its tokenURI(tokenId)
return value, otherwise returns the standard baseTokenURI + tokenId.
*/
function tokenURI(uint256 tokenId)
public
view
override
returns (string memory)
{
if (address(renderingContract) != address(0)) {
return renderingContract.tokenURI(tokenId);
}
return super.tokenURI(tokenId);
}
/**
#notice Sets the contract-wide royalty info.
*/
function setRoyaltyInfo(address receiver, uint96 feeBasisPoints)
external
onlyOwner
{
_setDefaultRoyalty(receiver, feeBasisPoints);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721ACommon, ERC2981, AccessControlEnumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
It compiles ok, but when I tried to create a test for minting, I must generate a valid signature...
This is the test
const { expect } = require('chai');
describe("TestBirds", function () {
it ("Should return correct name, URI, owner and beneficiary", async function () {
const [owner, addr1] = await hre.ethers.getSigners()
provider = ethers.provider
const TestBirdsContract = await hre.ethers.getContractFactory("TestBirds")
const testBirdsContractDeployed = await TestBirdsContract.deploy(
"TestBirds",
"APFP",
"https://test.url/",
owner.address,
owner.address)
console.log(await provider.getBalance(owner.address));
await testBirdsContractDeployed.deployed()
const nonce = await ethers.provider.getTransactionCount(owner.address, "latest")
await testBirdsContractDeployed.mintPublic(owner.address, nonce, signature???)
expect(await testBirdsContractDeployed.name()).to.equal("TestBirds")
expect(await testBirdsContractDeployed.tokenURI(0), "https://test.url/0")
expect(await testBirdsContractDeployed.ownerOf(0)).to.equal(owner.address)
})
})
How should I sing this in order to work? I can not test the contract without the sign. If I remove the signature param from the contract it works, but that is not what I want.
Thanks
It seems that the only missing was to add a signer. This seems to work for your test contract...
import { expect } from "chai";
import { ethers } from "hardhat";
describe("Test signature", function () {
it("deploy tester contract, and send signed message", async function () {
const TestSignature = await ethers.getContractFactory("TestSignature",owner);
const testSignature = await TestSignature.deploy();
await testSignature.deployed();
const [owner] = await ethers.getSigners();
// missing line
testSignature.addSigner(owner.address);
const params = ethers.utils.solidityPack(
["address", "uint256", "bytes32"],
[owner.address, "10", ethers.utils.keccak256("0x66")]
);
const signed = await owner.signMessage(params);
console.log("owner address", owner.address);
await testSignature.mint(
owner.address,
"10",
ethers.utils.keccak256("0x66"),
signed
);
});
});
I've attempted the following, but without much success yet:
import { expect } from "chai";
import { ethers } from "hardhat";
describe("Test signature", function () {
it("deploy tester contract, and send signed message", async function () {
const TestSignature = await ethers.getContractFactory("TestSignature");
const testSignature = await TestSignature.deploy();
await testSignature.deployed();
const [owner] = await ethers.getSigners();
const params = ethers.utils.solidityPack(
["address", "uint256", "bytes32"],
[owner.address, "10", ethers.utils.keccak256("0x66")]
);
const signed = await owner.signMessage(params);
console.log("owner address", owner.address);
await testSignature.mint(
owner.address,
"10",
ethers.utils.keccak256("0x66"),
signed
);
});
});
That is a test against the following contract:
pragma solidity >=0.8.0 <0.9.0;
// SPDX-License-Identifier: MIT
import "./SignatureChecker.sol";
contract TestSignature {
using EnumerableSet for EnumerableSet.AddressSet;
using SignatureChecker for EnumerableSet.AddressSet;
EnumerableSet.AddressSet internal signers;
constructor() {
signers.add(msg.sender);
}
mapping(bytes32 => bool) public usedMessages;
function mint(
address to,
uint256 price,
bytes32 nonce,
bytes calldata sig
) external payable {
signers.requireValidSignature(
signaturePayload(to, price, nonce),
sig,
usedMessages
);
}
/**
#dev Constructs the buffer that is hashed for validation with a minting signature.
*/
function signaturePayload(
address to,
uint256 price,
bytes32 nonce
) public pure returns (bytes memory) {
return abi.encodePacked(to, price, nonce);
}
function generateMessage(bytes memory data) public pure returns (bytes32) {
return SignatureChecker.generateMessage(data);
}
}
From what I understood, the signed message should match the message, and the ECDSA.toEthSignedMessageHash(data)
appends the \x19Ethereum Signed Message:\n to the beggining of the message. After it matches, it can recover the signer address, but it doesn't seem to work quite right yet.
I have a roulette smart contract, that uses another smart contract to provide it with random numbers. The issue i'm having is during compilation, i get the error:
TypeError: Type contract IRandomNumberGenerator is not implicitly convertible to expected type address.
project:/contracts/Roulette.sol:34:29:
randomNumberGenerator = IRandomNumberGenerator(randomNumberGenerator);
I'm not exactly sure where i'm going wrong, i've seen this code used in other contracts. Here is my full code, any help would be much appreciated.
// SPDX-License-Identifier: UNLICENSED"
pragma solidity ^0.8.7;
import "#openzeppelin/contracts/token/ERC20/IERC20.sol";
import "#openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "./IRandomNumberGenerator.sol";
contract Roulette is Ownable {
using SafeERC20 for IERC20;
IERC20 public gameToken;
uint256[100] internal randomNumbers;
IRandomNumberGenerator internal randomNumberGenerator;
// ensure caller is the random number generator contract
modifier onlyRandomGenerator() {
require(msg.sender == address(randomNumberGenerator), "Only random generator");
_;
}
constructor(address tokenAddress) {
gameToken = IERC20(tokenAddress);
}
function setRandomNumberGenerator(address randomNumberGenerator) external onlyOwner {
randomNumberGenerator = IRandomNumberGenerator(randomNumberGenerator);
}
function getRandomNumber() internal onlyOwner returns (uint) {
uint result = randomNumbers[randomNumbers.length-1];
delete randomNumbers[randomNumbers.length-1];
return result;
}
function numberGenerated(uint randomNumber) external onlyRandomGenerator {
randomNumbers = expand(randomNumber);
}
// generate 100 random numbers from the random number seed
function expand(uint256 randomValue) public pure returns (uint256[] memory expandedValues) {
expandedValues = new uint256[](100);
for (uint256 i = 0; i < 100; i++) {
expandedValues[i] = uint256(keccak256(abi.encode(randomValue, i)));
}
return expandedValues;
// TODO - ensure random numbers are roulette numbers
}
}
// SPDX-License-Identifier: UNLICENSED"
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
import "./IRoulette.sol";
contract RandomNumberGenerator is VRFConsumerBase {
address public roulette;
bytes32 internal keyHash;
uint256 internal fee;
uint256 internal randomResult;
// modifier to check if caller of owner is the admin
modifier onlyRoulette() {
require(msg.sender == roulette, "Caller to function is not the roulette contract");
_;
}
constructor(address _roulette)
VRFConsumerBase(
0xa555fC018435bef5A13C6c6870a9d4C11DEC329C, // VRF Coordinator
0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06 // LINK Token
)
{
keyHash = 0xcaf3c3727e033261d383b315559476f48034c13b18f8cafed4d871abe5049186;
fee = 0.1 * 10 ** 18;
roulette = _roulette;
}
function getRandomNumber() public onlyRoulette returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
return requestRandomness(keyHash, fee);
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
randomResult = randomness;
IRoulette(roulette).numberGenerated(randomResult);
}
}
Found out the issue was having the same name for both the parameter and the variable.
Updated the function to:
function setRandomNumberGenerator(address _randomNumberGenerator) external onlyOwner {
randomNumberGenerator = IRandomNumberGenerator(_randomNumberGenerator);
}
I am having issues verifying my smart contract on Rinkeby Testnet using #chainlink. My contract is deploying to Rinkeby fine but runs into issues when I have publish_source on. The contract verifies fine when using v0.6 and Solidity 0.6.6, I seem to only have issues with v0.8. Anyone know why this might be happening?
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
/**
* #title MyContract is an example contract which requests data from
* the Chainlink network
* #dev This contract is designed to work on multiple networks, including
* local test networks
*/
contract MyContract is ChainlinkClient, ERC721 {
using Chainlink for Chainlink.Request;
address constant RINKEBY_ORACLE =
0x3A56aE4a2831C3d3514b5D7Af5578E45eBDb7a40;
bytes32 constant RINKEBY_JOB_ID =
bytes32("187bb80e5ee74a139734cac7475f3c6e");
uint256 constant FEE = 0.1 * 10**18;
uint256 public tokenCounter;
bytes32 public data;
string public image_url;
struct Clip {
string name;
string url;
}
Clip[] public clips;
mapping(bytes32 => string) public requestIdToClipName;
mapping(bytes32 => address) public requestIdToSender;
mapping(bytes32 => string) public requestIdToTokenURI;
event requestedCollectible(bytes32 indexed requestId);
mapping(bytes32 => uint256) public requestToTokenId;
mapping(uint256 => string) public tokenIdToName;
mapping(uint256 => string) public tokenIdToImgUrl;
constructor(address _link) public ERC721("Tests", "TST") {
if (_link == address(0)) {
setPublicChainlinkToken();
} else {
setChainlinkToken(_link);
}
setChainlinkOracle(0x3A56aE4a2831C3d3514b5D7Af5578E45eBDb7a40);
tokenCounter = 0;
}
function getChainlinkToken() public view returns (address) {
return chainlinkTokenAddress();
}
function createRequestTo(
string memory _url,
string memory _path,
string memory clipName
) public returns (bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(
RINKEBY_JOB_ID,
address(this),
this.fulfill.selector
);
req.add("get", _url);
req.add("path", _path);
requestId = sendChainlinkRequestTo(RINKEBY_ORACLE, req, FEE);
requestIdToClipName[requestId] = clipName;
requestIdToSender[requestId] = msg.sender;
emit requestedCollectible(requestId);
return requestId;
}
function fulfill(bytes32 _requestId, bytes32 _data)
public
recordChainlinkFulfillment(_requestId)
{
address nftOwner = requestIdToSender[_requestId];
string memory name = requestIdToClipName[_requestId];
uint256 newItemId = clips.length;
data = _data;
image_url = bytes32ToString(data);
clips.push(Clip(name, image_url));
_safeMint(nftOwner, newItemId);
tokenIdToName[newItemId] = name;
tokenIdToImgUrl[newItemId] = image_url;
}
function getNumberOfClips() public view returns (uint256) {
return clips.length;
}
/**
* #notice Allows the owner to withdraw any LINK balance on the contract
*/
function withdrawLink() public {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(
link.transfer(msg.sender, link.balanceOf(address(this))),
"Unable to transfer"
);
}
/**
* #notice Call this method if no response is received within 5 minutes
* #param _requestId The ID that was generated for the request to cancel
* #param _payment The payment specified for the request to cancel
* #param _callbackFunctionId The bytes4 callback function ID specified for
* the request to cancel
* #param _expiration The expiration generated for the request to cancel
*/
function cancelRequest(
bytes32 _requestId,
uint256 _payment,
bytes4 _callbackFunctionId,
uint256 _expiration
) public {
cancelChainlinkRequest(
_requestId,
_payment,
_callbackFunctionId,
_expiration
);
}
}
Here is the script I am using to deploy:
from brownie import MyContract, accounts, network, config
from scripts.helpful_scripts import fund_contract
def main():
dev = accounts.add(config['wallets']['from_key'])
print(network.show_active())
publish_source = True
my_contract = MyContract.deploy(
config['networks'][network.show_active()]['link_token'],
{"from": dev},
publish_source=publish_source
)
fund_contract(my_contract)
return my_contract
This is the error:
Transaction sent: 0x01ef409a5b0c2f6de60194f14f1d2e8ffac9a6ce6397aaa38392d949e2656909
Gas price: 1.000000008 gwei Gas limit: 2793499 Nonce: 359
MyContract.constructor confirmed Block: 9332417 Gas used: 2539545 (90.91%)
MyContract deployed at: 0x42fBa06808C3cB11e5D7AcC6BB02B1A612e040d1
File "brownie/_cli/run.py", line 49, in main
return_value, frame = run(
File "brownie/project/scripts.py", line 103, in run
return_value = f_locals[method_name](*args, **kwargs)
File "./scripts/deploy_api.py", line 11, in main
my_contract = MyContract.deploy(
File "brownie/network/contract.py", line 600, in __call__
return tx["from"].deploy(
File "brownie/network/account.py", line 555, in deploy
contract.publish_source(deployed_contract, silent=silent)
File "brownie/network/contract.py", line 410, in publish_source
contract_info = self.get_verification_info()
File "brownie/network/contract.py", line 292, in get_verification_info
if symbol_alias["local"] is not None:
KeyError: 'local'
Two points going on here:
Brownie Etherscan verification doesn't work well with Solidity v0.8. Work is being done to fix this issue.
Looks like you are deploying to a local network instead of Rinkeby. Either remove publish_source when deploying locally or add --network rinkeby to the end of your deploy script to deploy to Rinkeby.