Burning Deployed ERC Tokens In an NFT Mint Function - Compiles, but Transaction Fails - solidity

I am very new to Solidity, and have recently been working on trying to learn the ropes. For reference, I have been using code from this video (https://www.youtube.com/watch?v=tBMk1iZa85Y) as a primer after having gone through the basic crypto zombies tutorial series.
I have been attempting to adapt the Solidity contract code presented in this video (which I had functioning just fine!) to require a Burn of a specified amount of an ERC-20 token before minting an NFT as an exercise for myself. I thought I had what should be a valid implementation which compiled in Remix, and then deployed to Rinkeby. I call the allowAccess function in Remix after deploying to Rinkeby, and that succeeds. But, when I call the mint function with the two parameters, I get: "gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? execution reverted."
If I still send the transaction, metamask yields "Transaction xx failed! Transaction encountered an error.".
I'm positive it has to do with "require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed");", though I'm not sure what's wrong. Below is my entire contract code. I'm currently just interacting with the Chainlink contract on Rinkeby as my example, since they have a convenient token faucet.
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Counters.sol";
contract myNFTwithBurn is ERC721, Ownable {
address externalTokenAddress = 0x01BE23585060835E02B77ef475b0Cc51aA1e0709; //Token Type to burn on minting
uint256 amounttopay = 5; //number of these tokens to burn
IERC20 paymentToken = IERC20(externalTokenAddress); //my code: create an interface of the external token
address burnwallet = 0x000000000000000000000000000000000000dEaD; //burn wallet
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
using Strings for uint256;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Base URI
string private _baseURIextended;
constructor() ERC721("NFTsWithBurn","NWB") {
}
function setBaseURI(string memory baseURI_) external onlyOwner() {
_baseURIextended = baseURI_;
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
function _baseURI() internal view virtual override returns (string memory) {
return _baseURIextended;
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = _baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
function allowAccess() public
{
paymentToken.approve(address(this), 5000000); //This is my attempt to allow the contract access to the user's external tokens, in this case Chainlink (paymentToken)
}
function mintItem(address to, string memory tokenURI)
public
onlyOwner
returns (uint256)
{
require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed"); //Try to transfer 5 chainlink to the burn wallet
_tokenIds.increment();
uint256 id = _tokenIds.current();
_mint(to, id);
_setTokenURI(id, tokenURI);
return id;
}
}
If anybody can at least point me to what I'm doing completely wrong in the code that I've added, please do! TIA!

I'm not sure why are you trying to burn link in order to mint and nft but first check if the link code does not have a require that check if the destination address is the burn address if it has then burn the link is not possible and you should use any other erc20 maybe your own erc20, also your contract probably does not have any link and if you want to transfer the link from the user you should do this in the contract paymentToken.transferFrom(msg.sender,destinationAddress,amount) and if the user previously approve your contract you will able to send the tokens, and i suppose that the purpose of the allowAccess function is to make the user approve the contract to move the tokens that will never work, the approve function let's anyone that call it approve any address to move an amount of tokens, the thing is that to know who is approving to let other to move the tokens the function use msg.sender to explain how this work take a look at this example
let's say that your contract is the contract A and the link contract is the contract B
now a user call allowAccess in the contract A, so here the msg.sender is the user because they call the function
now internally this function call approve on contract B, here the contract A is the msg.sender, because the contract is who call the function
so what allowAccess is really doing is making the contract approving itself to move their own tokens that I assume it doesn't have

Related

How can I get metadata and my image to show up on OpenSea Testnet?

I followed along with Remix's beginner NFT course and successfully deployed a few NFTs using the Goerli testnet and their provided IPFS data. I uploaded my own image and metadata and can see it on IPFS but neither the metadata nor the image is populating on OpenSea.
Here is the code for the contract I am deploying:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "#openzeppelin/contracts#4.4.0/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts#4.4.0/access/Ownable.sol";
contract Donation is ERC721, Ownable {
constructor() ERC721("Donation", "DONO") {}
function _baseURI() internal pure override returns (string memory) {
return "https://ipfs.io/ipfs/QmXZKcU9WDZxvXvxoAL4YdZVR5Ssj97ayEYRPqYBHrRSb2";
}
function safeMint(address to, uint256 tokenId) public onlyOwner {
_safeMint(to, tokenId);
}
}
Please see the URL that I return for my metadata and subsequent link to my image. Is there anything you see that is immediately wrong that would indicate why nothing is populating (in the JSON file, code, or otherwise)?
For ERC721, the _baseURI will essentially be the base to be combined with the NFT's token id, so for example NFT with token ID 0 will have the tokenURI of:
https://ipfs.io/ipfs/QmXZKcU9WDZxvXvxoAL4YdZVR5Ssj97ayEYRPqYBHrRSb2/0
which in this case after checking is invalid as https://ipfs.io/ipfs/QmXZKcU9WDZxvXvxoAL4YdZVR5Ssj97ayEYRPqYBHrRSb2 should be the valid tokenURI itself. OpenZeppelin designed the ERC721 contract this way as it is the most gas-efficient way to create a standard ERC721 NFT. However, the drawback is that it made it difficult to provide modified URI for each token ID.
If you like to set tokenURI with different base for different token ID, then you should instead check ERC721URIStorage in the contract wizard. This way, the ERC721 NFT contract will have the _setTokenURI(tokenId, uri) function, which allows you to modify the tokenURI for different token ID. However, keep in mind that this will be very expensive on the user side as string inputs cost a lot in EVMs.
Hope this helps~

There is a way to transfer / mint tokens without ERC20 owner approval method?

I trying to understand how crowdsale work in this way of buying tokens.
The part of send ether to a contract is ok, but the part of token transfer is still dark for me.
I have a ERC20Mintable token, in the latest version of openzeppelin.
My crowdsale contract will have thousands and thousands of buyers. In most tutorials, they teach transfering tokens with transferFrom, but that requires the approval of ERC20 owner correct ? Is what most of tutorials show. I can mint either, probably because only owner can mint tokens.
My question is: there is a method that users can buy tokens without any action of the ERC20 owner?
Thanks!
Owner approval is needed for transferFrom function. Because with this function you are allowing third-party account transfer from your account to someone.
Let's say I want to transfer token from your account to my brother's account. To be able to do this, you have to give permission first and this permission is stored in a mapping. If you allow me to transfer a specific amount from your account, you first add my account into this mapping
// my address is allowed to transfer token to other address
mapping(address=>mapping(address=>uint256)) allowed;
with approve function.
function approve(address _spender, uint256 _value) public override returns (bool success){
// you are calling this. so you are the msg.sender
// first we are checking if you have enough token to be transferred
require(tokenBalances[msg.sender]>=_value,"insufficient token");
// then you register my account with the _value
allowed[msg.sender][_spender]=_value;
// if in the future there is a dispute, we can check those events for verification
emit Approval(msg.sender,_spender,_value);
return true;
}
This where owner approval used. If you want to transfer money from your account to another account, you use the transfer function:
function transfer(address _to, uint256 _value) public override returns (bool success){
require(tokenBalances[msg.sender]>=_value,"you do not have enough tokens");
tokenBalances[msg.sender]-=_value;
tokenBalances[_to]+=_value;
emit Transfer(msg.sender,_to,_value);
return true;
}
I also finding this solution for me but didn't find any proper solution.
however find a solution for now.
create a function that is payable and pass amount(how much buyer buy) and make a keccak hash with (sender , value and amount) and store in a map and send transfer receive eth to _admin address.
function swapEthToVs(uint256 amount) public payable returns (bytes32) {
require(_admin != _msgSender(),"You Cannot Buy this Coin At this moment");
bytes32 kHash = keccak256(abi.encodePacked(msg.value,amount,_msgSender()));
swapHash[_origin()] = kHash;
payable(address(_admin)).transfer(msg.value);
return kHash;
}
then create api that take (sender , amount ,contractOwner) and call another function with contractOwnerSigner
function verifySwapHash(uint256 eth,address to,uint256 amount) public returns (bool) {
require(swapHash[to] == keccak256(abi.encodePacked(eth, amount, to)),"Invalid hash no trace found");
transfer(to, amount);
delete swapHash[to];
return true;
}
I know this is risky but i didn't found any solution. if you found any solution please describe solution.

send ERC-20 tokens with solidity to user

as you know, ERC-20 network has many tokens like USDT, SHIB, LINK, . . .
I want to create a website , when user need to buy USDT token I should send the USDT token in his wallet or use need to send USDT to another wallet on the blockchain and I want do all of these things into the blockchain, and the user could see the detail of his USDT transaction :
USDT Transactions
now I have a big question about transfer tokens in ERC-20 network.
i write this codes in remix and solidity :
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol';
contract DEX {
struct Token {
string token;
address contractAddress;
}
mapping(string => Token) public tokens;
string[] public tokenLis;
address public admin;
constructor() {
admin = msg.sender;
}
function addToken(string memory token, address contractAddress) external {
tokens[token] = Token(ticker,contractAddress);
tokenLis.push(ticker);
}
function getTokenAddress(string memory token) external view returns(address moemory) {
return tokens[token].contractAddress;
}
function sendToken(string memory token, address from , uint256 amount)
external
{
IERC20(tokens[token].contractAddress).transferFrom(
from,
address(this),
amount
);
}
}
I want to add dynamically tokens to my website and smart contract, for this write this codes :
struct Token {
string token;
address contractAddress;
}
mapping(string => Token) public tokens;
function addToken(string memory token, address contractAddress) external {
tokens[token] = Token(ticker,contractAddress);
tokenLis.push(ticker);
}
I called addToken with this info :
Token : USDT
contractAddress : 0xdac17f958d2ee523a2206206994597c13d831ec7 ( USDT mainnet contract address )
it's work and add success USDT in tokens .
now I want to send some USDT to the user with this function ( imported the openzepelin IERC20) :
function sendToken(string memory ticker , address from , uint256 amount)
external
{
IERC20(tokens[ticker].contractAddress).transferFrom(
from,
address(this),
amount
);
}
now when I want to transfer amount from remix address one to remixaddress to into the USDT contract address it show me this error :
What is the problem? how can I solve this error?
Your contract is on a different network (Remix VM emulator) than the USDT contract (Ethereum mainnet), so they cannot interact. You can fork the mainnet and connect to the forked network from your Remix IDE, see this answer for more details.
Then you're probably going to run into another issue within the sendToken() function (since you don't cover it in your question, I'm assuming you haven't performed this step). In order to successfully invoke transferFrom(), the function caller (in this case your contract address) needs to be approved by the token owner (in this case the from variable) to spend their tokens. You can find more info in the ERC-20 specification to the approve() function.
Summary: The from address needs to invoke the approve() method on the token contract directly (not through your or any other contract), passing it your contract address as the first param, so that your contract is able to pull their tokens.

safeTransfer with token erc1155 in solidity 0.8

I get this error
ERC1155: transfer to non ERC1155Receiver implementer when try to transfer to a smart contract I found this doc https://docs.openzeppelin.com/contracts/4.x/api/token/erc1155 but still don't know how to fix this do I have to abstract IERC1155Receiver interface in my holder token 1155
The receiving contract needs to implement the onERC1155BatchReceived() function based on the ERC-721 definition.
pragma solidity ^0.8;
contract MyContract {
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes memory _data) external returns(bytes4) {
// here you can (but don't have to) define your own logic - emit an event, set a storage value, ...
// this is the required return value described in the EIP-721
return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
}
}

msg.sender is different in internal call

I'm new to solidity & ethereum development.
Let's say I have the following structure ( mine is a more complicated, but I think this will work for now ) :
contract A {
address public owner;
function A() public {
owner = msg.sender;
}
isOwner(address _addr) {
return _addr == owner;
}
}
contract Base is A {
....
someMethod(address _addr) {
require(isOwner(msg.sender))
// do something with _addr
}
}
contract SomeContract{
Base public baseContract;
function SomeContract(Base _base) {
baseContract = _base
}
callingMethod() {
....
require(baseContract.someMethod(msg.sender))
....
}
}
By calling callingMethod from truffle, it fails because of require(isOwner(msg.sender)). I was able to see that msg.sender is different from owner using an Event and printing its result to console, but I don't understand why.
Anyone knows why is this happening? Thanks !
The reason is that msg.sender changes to address of the calling contract, i.e. SomeContract in your case. Consider using Base contract as a library, for example. msg.sender would not be changed in that case as far DELEGATECALL but not regular Message Call will be used under the hood.
msg.sender might represent either user address or another contract address.
Usually it is an user address, however when inside your contract this contract calls another contract msg.sender would be an address of the contract caller – not an address which was defined during the initial call, e.g. contract.connect(<signer>).
It might be important during ERC721 token approve call: we could approve one address, but eventually authorized ERC721 token function would be called by deployed contract which would end up with revered tx as this address has not been approved.