How do I accept an ERC20 token for Nft mint payment instead of ETH - solidity

How do I accept an ERC 20 token for minting NFTS on my smart contract. I’m currently paying for the minting using ETH, and I will like to accept another ERC 20 token.

You can change your mint function in order to accept the preferred token type as payment. And in order to prevent paying gas in eth, you can use biconomy forward.Check out their docs.
https://docs.biconomy.io/products/enable-paying-gas-in-erc20

You need a reference from the NFT contract to your token contract.
Let's say we have these two contracts: NFTContract, TokenContract.
contract NFTContract {
TokenContract tokenContract;
...
mint() {
tokensContract.transfer(msg.sender(), addressReceiver, tokensToSend);
...
}
}
contract TokenContract {
transfer(sender, receiver, balance) {}
}
You can check an example of how I did this in my repo I'm developing for my thesis.
https://github.com/NickVanzo/Contracts_For_Thesis

Maybe it will work for you but be careful you can take errors for solidity versions.
pragma solidity ^0.8.9;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract LSTNFT is ERC721, ERC721Burnable, Ownable {
uint256 public cost;
uint256 maxSupply = 20;
uint256 maxMintAmountPerTx = 2;
// address of the ERC-20 contract
address public erc20Contract = "ERC-20 address is here";
// instance of the ERC-20 contract
ERC20 public erc20;
constructor() ERC721("Lestonz NFT", "LSTN") {
erc20 = ERC20(erc20Contract);
}
function _baseURI() internal pure override returns (string memory) {
return "ipfs://your ipfs url";
}
modifier mintPriceCompliance(uint256 _mintAmount) {
if (msg.sender != owner()) {
require(msg.value >= cost * _mintAmount, 'Insufficient funds!');
}
_;
}
function mint(uint256 _mintAmount) public payable mintPriceCompliance(_mintAmount) {
require(erc20.balanceOf(msg.sender) >= msg.value, "Not enough ERC-20 tokens");
_safeMint(_msgSender(), _mintAmount);
}
function setCost(uint256 _cost) public onlyOwner {
cost = _cost;
}
}

Related

How to deposit eth to contract writen in solidity on front-end app using useDapp

Given i have this smart contract in remix
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract DepositWithdraw {
mapping(address => uint256) private balances;
event Deposit(address indexed depositor, uint256 amount);
event Withdrawal(address indexed withdrawer, uint256 amount);
function deposit() public payable {
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
emit Withdrawal(msg.sender, amount);
}
function balanceOf(address account) public view returns (uint256) {
return balances[account];
}
}
Deposit by remix ide works, but when I'm trying to deposit using useDapp lib in javascript by using useContractFunction I can't pass the amount to deposit. When I'm redirected to meta mask to accept transaction there is no amount shown.
my front-end code:
import { Contract } from 'ethers';
import { Interface } from 'ethers/lib/utils';
import { useContractFunction } from '#usedapp/core';
const contractAddress = // my contract address;
const ABI = new Interface(
//...copied contract ABI from remix
);
const contract = new Contract(contractAddress, ABI);
const { send } = useContractFunction(contract, 'deposit');
// send(value) will error that deposit function argument length is no correct (it want 0 arguments)
tried adding third argument with options to useContractFunction but no success.
When i call send without arguments it will process to metamask but no amount shown, only gas fees
any ideas?

OpenZeppelin ERC721URIStorage _setTokenURI

i'm trying to create a smart contract for minting NFT's. When i try to import and use _setTokenUri from OpenZeppelin, there is an error accours in Remix ide says that "Undeclared identifier". I wonder why, so here is my code ;
pragma solidity ^0.8.9;
import '#openzeppelin/contracts/token/ERC721/ERC721.sol';
import '#openzeppelin/contracts/access/Ownable.sol';
import '#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';
contract MintNft is ERC721, Ownable {
uint256 public mintPrice = 0.005 ether;
uint public totalSupply;
uint public maxSupply;
bool public isMintEnabled;
// Mapping for amount of minted nfts by an address
mapping(address => uint256) public mintedWallets;
constructor() payable ERC721('Pokemon Card', 'POKE') {
maxSupply = 20;
}
function toggleIsMintEnabled() external onlyOwner {
isMintEnabled = !isMintEnabled;
}
function setMaxSupply(uint256 _maxSupply) external onlyOwner {
maxSupply = _maxSupply;
}
function mint(string memory tokenURI) external payable {
require(isMintEnabled,"Minting is not enable");
require(mintedWallets[msg.sender] < 2, 'You have reached maximum mint number');
require(msg.value == mintPrice, "Wrong value");
require(maxSupply > totalSupply, "Sold Out ! ");
mintedWallets[msg.sender]++;
totalSupply++;
uint256 tokenId = totalSupply;
_safeMint(msg.sender, tokenId);
_setTokenURI(tokenId,tokenURI);
}
}
Thanks already.
Okay i figured out that i forget to implement ERC721URIStorage when naming my contract.
i changed it to contract MintNft is ERC721URIStorage, Ownable
Now it works fine.

ERC2981 NFT Royalties Implementation in a ERC721 Marketplace

I've read already many posts regarding this topic, but I just can't wrap my head around this Royalty implementation thing.
Pls help me out, I don't know how I should play with this thing.
what is feeNumerator? _setDefaultRoyalty(address receiver, uint96 feeNumerator)
is it NFT Price? Nft Royalty Percent?
Pls take a look at the following contract and help me with how I can implement NftRoyalty in this contract. Am I doing it right? if not then pls correct me. Thanks.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.8;
import "hardhat/console.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/token/common/ERC2981.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
contract PodShip is ERC721URIStorage, ERC2981, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _podcastId;
Counters.Counter private _nftId;
struct Podcast {
address nftCreator;
address nftOwner;
string nftName;
string nftDescription;
uint256 nftPrice;
uint256 nftRoyaltyPercent;
uint256 nftId;
bool listed;
}
mapping(uint256 => Podcast) podcastId;
constructor() ERC721("PodShip NFTs", "PODSHIP") {}
function mintNft(
string memory ipfsURI,
string memory _nftName,
string memory _nftDescription,
uint256 _nftPrice,
uint256 _nftRoyaltyPercent) public returns(uint256) {
require(_nftPrice > 0, "NFT Price not set correctly");
require(_nftRoyaltyPercent > 0 && _nftRoyaltyPercent <= 50, "NFT Royalty should be in-between 1 & 50");
_podcastId.increment();
_nftId.increment();
uint256 nft_Id = _nftId.current();
_safeMint(msg.sender, nft_Id);
_setTokenURI(nft_Id, ipfsURI);
podcastId[_podcastId.current()] = Podcast(
payable(msg.sender),
payable(msg.sender),
_nftName,
_nftDescription,
_nftPrice,
_nftRoyaltyPercent,
nft_Id,
false
);
console.log(nft_Id);
console.log(_podcastId.current());
return nft_Id;
}
function listNFT(uint256 _podcastID) public {
require(msg.sender == podcastId[_podcastID].nftOwner && msg.sender == podcastId[_podcastID].nftCreator, "Only Owner can list his NFT");
podcastId[_podcastID].listed = true;
approve(address(this), podcastId[_podcastID].nftId);
setApprovalForAll(address(this), true);
_setDefaultRoyalty(podcastId[_podcastID].nftCreator, uint96(podcastId[_podcastID].nftRoyaltyPercent));
}

how to write a contract to set a reward for players and they claim the reward?

I want to create a contract to give the players rewards and they claim their rewards.
It seems everything is ok!
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
import "#openzeppelin/contracts/token/ERC20/IERC20.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract Rewards is Ownable {
IERC20 public rewardsToken;
mapping(address => uint) public rewards;
constructor(address _rewardsToken) {
rewardsToken = IERC20(_rewardsToken);
}
function setReward(address account,uint256 amount) public onlyOwner {
rewards[account] = amount;
}
function claimReward() public{
uint256 reward = rewards[msg.sender];
rewards[msg.sender] = 0;
rewardsToken.transfer(msg.sender, reward);
}
}
but I don't know why when I claim the reward nothing happens and I have this errors.
how can I send tokens to my contract?
rewardsToken.transfer(msg.sender, reward);
This snippet fails because the Rewards contract address doesn't hold sufficient token balance.
Solution: Send at least the reward amount of tokens to the Rewards contract address, so that the contract can transfer them to the user.
stakingToken = contract address token
rewardsToken = wallet where you have the tokens
Note: You need approve() the smart contract address and amount after created this
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
import "#openzeppelin/contracts/token/ERC20/IERC20.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract Rewards is Ownable {
IERC20 public stakingToken;
IERC20 public rewardsToken;
mapping(address => uint) public rewards;
constructor(address _stakingToken, address _rewardsToken) {
stakingToken = IERC20(_stakingToken);
rewardsToken = IERC20(_rewardsToken);
}
function setReward(address account,uint256 amount) public onlyOwner {
rewards[account] = amount;
}
function claimReward() public{
uint256 reward = rewards[msg.sender];
rewards[msg.sender] = 0;
stakingToken.transferFrom(address(rewardsToken), msg.sender,reward);
}
}

Solidity calling contract with elevated permissions

I have two contracts, one for handling staking and one for minting a NFT. The flow I want is for the user to stake in frontend react app which will invoke the staking contract. The user will then be eligible to mint a NFT when staked.
Now the issue I am facing is that because the minting role is called from stakingExample contract, which requires the user to invoke this, but as it has a critical function (mint) of the other contract, it should be protected with permissions such that only StakingExample can call NFTExample contract.
Is there a way to allow the user to run NFTExample with elevated permissions temporary in smart contract?
Example of staking contract:
// SPDX-License-Identifier: unlicensed
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/access/AccessControl.sol";
import "#openzeppelin/contracts/token/ERC20/IERC20.sol";
import "#openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract StakingExample is AccessControl {
bytes32 public constant CONTRACT_ROLE = keccak256("CONTRACT_ROLE");
NFTExample public _NFTExample;
...
function someStakingFunction() {
// code that stakes and
// set some variable that tracks if user have minted
}
function claimNFT(uint256 _pid, string memory _tokenURI) public onlyRole(CONTRACT_ROLE) {
// checks if user have unclaimed NFT
if (haveUnclaimed) {
_NFTExample.mintItem(msg.sender, _tokenURI)
}
}
}
Example of NFT contract:
// SPDX-License-Identifier: unlicensed
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/access/AccessControl.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
contract CMRGachaSeedNFT is ERC721URIStorage, AccessControl, ERC721Enumerable {
bytes32 public constant CONTRACT_ROLE = keccak256("CONTRACT_ROLE");
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
...
// Only Contract Role can call mint item, which mint item and transfers it to user's address
function mintItem(address _address, string memory _tokenURI)
public
onlyRole(CONTRACT_ROLE)
returns (uint256)
{
// Do some checks
// Mint
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(_address, newItemId);
_setTokenURI(newItemId, _tokenURI);
return newItemId;
}
}
You need to add one function in staking contract which shows amount of the staking:
function showStakeAmount() external view returns(uint256){
//I don't know your codes about this but you need a mapping to store
//the stake amount of each user and here you return it but something like this:
return StakingAmountOfUsers(msg.sender);
}
Then you need an interface of the staking contract and its address, also make an modifier in NFT contract (Following changes must be added):
interface StakingInterface{
function showStakeAmount() external view returns(uint256);
}
contract CMRGachaSeedNFT is ERC721URIStorage, AccessControl, ERC721Enumerable {
uint256 AmountThatShouldBeStaked;
StakingInterface StakingContract;
constructor(address STAKING_CONTRACT_ADDRESS){
StakingContract = StakingInterface(STAKING_CONTRACT_ADDRESS);
}
modifier isStaked(){
require(StakingContract.showStakeAmount() > AmountThatShouldBeStaked, "You did not stake enough amount of X token");
_;
}
function mintItem(address _address, string memory _tokenURI)
public
onlyRole(CONTRACT_ROLE)
returns (uint256)
isStaked()
{
//Continue coding...
}
}