How do I resolve Slither warning about the low level call in the following method:
// A proposer calls function and if address has an allowance, recieves ETH in return.
function getPayout(address payable addressOfProposer)
public
returns (bool)
{
// Get the available allowance first amd store in uint256.
uint256 allowanceAvailable = _payoutTotals[addressOfProposer];
require(allowanceAvailable > 0, "You do not have any funds available.");
_decreasePayout(addressOfProposer, allowanceAvailable);
(bool sent, ) = addressOfProposer.call{value: allowanceAvailable}("");
require(sent, "Failed to send ether");
// console.log("transfer success");
emit Withdraw(addressOfProposer, allowanceAvailable);
return true;
}
Use //slither-disable-next-line DETECTOR_NAME:
// A proposer calls function and if address has an allowance, recieves ETH in return.
function getPayout(address payable addressOfProposer)
public
returns (bool)
{
// Get the available allowance first amd store in uint256.
uint256 allowanceAvailable = _payoutTotals[addressOfProposer];
require(allowanceAvailable > 0, "You do not have any funds available.");
_decreasePayout(addressOfProposer, allowanceAvailable);
//slither-disable-next-line unchecked-lowlevel
(bool sent, ) = addressOfProposer.call{value: allowanceAvailable}("");
require(sent, "Failed to send ether");
// console.log("transfer success");
emit Withdraw(addressOfProposer, allowanceAvailable);
return true;
}
It's interesting that it gives an unchecked low-level call warning, however, given that it does appear to be checked.
Related
I am trying to send ether from contract to a wallet, when I use the call function with msg.value it works, however when I try to send 1 ether instead of msg.value it doesn't work. Why is that and how to overcome this? I am using Remix.io Javascript VM London, can that be the reason?
Here this code below works:
function sendMoney() public payable {
address payable receiver = payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2);
(bool sent, bytes memory data) = receiver.call{ value: msg.value }("");
require(sent, "Failed to send Ether");
}
However this code below doesn't work. I have to send fixed amount of ether.
function sendMoney() public payable {
address payable receiver = payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2);
(bool sent, bytes memory data) = receiver.call{ value: 1 ether }("");
require(sent, "Failed to send Ether");
}
sendMoney() public payable {
address payable receiver =
payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2);
(bool sent, bytes memory data) = receiver.call{ value: 1 ether }("");
require(sent, "Failed to send Ether");
}
This works. You just need to pass something in "value" in remix, and after 1 ether will be transfered to 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 and value that you passed in remix will be transfered to contract balance.
Can someone explain to me how I can set approval for ERC721 contract ?
1- At first, the token once minted belongs to the marketplace
2- with buy, I need to transfer ownership of the token to the caller
I keep getting these errors
X Fail with error 'ERC721: approve caller is not token owner nor approved for all'
x Fail with error 'ERC721: approve to caller'
This is my smart contract : the mint buy function
//The first time a token is created, it is listed here
function createToken(string memory tokenURI, string memory name, uint256 price) public payable returns (uint) {
uint256 currentTokenId = _tokenIds.current();
//Mint the NFT with tokenId newTokenId to the address who called createToken
_safeMint(msg.sender, currentTokenId);
//Map the tokenId to the tokenURI (which is an IPFS URL with the NFT metadata)
_setTokenURI(currentTokenId, tokenURI);
//Helper function to update Global variables and emit an event
_create(currentTokenId, tokenURI, name, listPrice);
setApprovalForAll(address(this), true);
//Increment the tokenId counter, which is keeping track of the number of minted NFTs
_tokenIds.increment();
uint256 newTokenId = _tokenIds.current();
return newTokenId;
}
function _create(uint256 tokenId, string memory tokenURI, string memory name, uint256 price) private {
//Make sure the sender sent enough ETH to pay for listing
require(msg.value == listPrice, "Hopefully sending the correct price");
//Just sanity check
require(price > 0, "Make sure the price isn't negative");
//Update the mapping of tokenId's to Token details, useful for retrieval functions
idToToken[tokenId] = Token(
tokenId,
tokenURI,
name,
payable(address(this)),
price,
true
);
_transfer(msg.sender, address(this), tokenId);
//Emit the event for successful transfer. The frontend parses this message and updates the end user
emit TokenListedSuccess(
tokenId,
address(this),
price,
true
);
}
function buyNFT(uint256 tokenId) public payable {
require(msg.value > 0, "You need to send some ether");
require(msg.value == idToToken[tokenId].price, "Please submit the asking price in order to complete the purchase");
approve(msg.sender, tokenId);
setApprovalForAll(msg.sender, true);
transferFrom(address(this), msg.sender,tokenId);
// safeTransferFrom(address(this), msg.sender, tokenId);
payable(idToToken[tokenId].owner).transfer(msg.value);
//update the details of the token
idToToken[tokenId].owner = payable(msg.sender);
idToToken[tokenId].isForSale = false;
_itemsSold.increment();
//Transfer the proceeds from the sale to the seller of the NFT
payable(address(this)).transfer(msg.value);
// emit Event
}
this is my client app :
const buyToken = (...args) => {
const [tokenId] = args
return new Promise(async resolve => {
try {
let transaction = await contract.buyNFT(tokenId, {
gasLimit: 5500000,
value: price.toString()
});
await transaction.wait();
resolve()
} catch (e) {
console.error(e)
}
})
}
I think the issue is with
transferFrom(address(this), msg.sender,tokenId);
You are transferring from the contract address to the msg.sender but it should be from the nft owner or token id owner.
The approve() function grants a third party the ability to transfer a
single token id. setApprovalForAll() will grant a third party the
ability to transfer all of the tokens you own for a given collection.
When we mint the token we call setApprovalForAll() to authorize the marketplace to transfer our token. This is the trasferFrom
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
Before transferring it is checking if the msg.sender is already authorized for this transaction or from address is the "owner" of token. To solve the issue get the owner of the tokenId. Since you are passing the "tokenIdto thebuyNFT`
address owner=ERC721.ownerOf(tokenId);
transferFrom(owner, msg.sender,tokenId);
I have the following smart contract:
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./MyCoinSupply.sol";
contract MyCoinDEX
{
IERC20 public token;
event Bought(uint256 amount);
event Sold(uint256 amount);
constructor() public
{
token = new MyCoinSupply();
}
function getSenderAddress() public view returns (address) // for debugging purposes
{
return (msg.sender);
}
function getAddress() public view returns (address)
{
return address(this);
}
function getTokenAddress() public view returns (address)
{
return address(token);
}
function buy() payable public // send ether and get tokens in exchange; 1 token == 1 ether
{
uint256 amountTobuy = msg.value;
uint256 dexBalance = token.balanceOf(address(this));
require(amountTobuy > 0, "You need to send some ether");
require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
token.transfer(msg.sender, amountTobuy);
emit Bought(amountTobuy);
}
function sell(uint256 amount) public // send tokens to get ether back
{
require(amount > 0, "You need to sell at least some tokens");
uint256 allowance = token.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance");
token.transferFrom(msg.sender, address(this), amount);
// https://stackoverflow.com/questions/67341914/error-send-and-transfer-are-only-available-for-objects-of-type-address-payable
payable(msg.sender).transfer(amount);
emit Sold(amount);
}
}
if I call the buy() method from truffle console, it executes without any exceptions:
truffle(development)> MyCoinDEX.buy({value: 1})
I verified that the account calling the buy() method receives the token. However, the balance of Ether in Ganache for the account calling the buy() method doesn't decrease. So essentially, the account is getting tokens for free.
What's going on here? How do I fix it?
I am not sure but it would be necessary to investigate about the balance of the contract account. The gas for token.transfer might be paid by the contract account balance, since the contract account is the transaction sender for token contract.
Or the balance decrement would be unnoticeable because it is too small.
I would be happy to know about the answer if you have found out.
I'm writing an NFT smart contract using the OpenZeppelin ERC721Full contract. I'm able to mint NFTs, but I want to have a button that enables them to be bought. I'm trying writing this function:
function buyNFT(uint _id) public payable{
//Get NFT owner address
address payable _seller = ownerOf(_id);
// aprove nft sell
approve(_seller, _id);
setApprovalForAll(msg.sender, true);
//transfer NFT
transferFrom(_seller, msg.sender, _id);
// transfer price in ETH
address(_seller).transfer(msg.value);
emit NftBought(_seller, msg.sender, msg.value);
}
This does not work because function approve must be called by the owner or an already approved address. I have no clue on how a buy function should be built. I know that I must use some requirements but first I want the function to work on tests and then I'll write the requirements.
How should a buy function be coded? Because the only solution I have found is to overwrite the approve function and omit the require of who can call this function. But it looks like it isn't the way it should be done.
Thank you!
You can use just the _transfer() function, see my buy() function for an example of implementation.
The approvals for sale can be done using a custom mapping - in my example tokenIdToPrice. If the value is non-zero, the token ID (mapping key) is for sale.
This is a basic code that allows selling an NTF. Feel free to expand on my code to allow "give away for free", "whitelist buyers" or any other feature.
pragma solidity ^0.8.4;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol';
contract MyToken is ERC721 {
event NftBought(address _seller, address _buyer, uint256 _price);
mapping (uint256 => uint256) public tokenIdToPrice;
constructor() ERC721('MyToken', 'MyT') {
_mint(msg.sender, 1);
}
function allowBuy(uint256 _tokenId, uint256 _price) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
require(_price > 0, 'Price zero');
tokenIdToPrice[_tokenId] = _price;
}
function disallowBuy(uint256 _tokenId) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
tokenIdToPrice[_tokenId] = 0;
}
function buy(uint256 _tokenId) external payable {
uint256 price = tokenIdToPrice[_tokenId];
require(price > 0, 'This token is not for sale');
require(msg.value == price, 'Incorrect value');
address seller = ownerOf(_tokenId);
_transfer(seller, msg.sender, _tokenId);
tokenIdToPrice[_tokenId] = 0; // not for sale anymore
payable(seller).transfer(msg.value); // send the ETH to the seller
emit NftBought(seller, msg.sender, msg.value);
}
}
How to simulate the sale:
The contract deployer (msg.sender) gets token ID 1.
Execute allowBuy(1, 2) that will allow anyone to buy token ID 1 for 2 wei.
From a second address, execute buy(1) sending along 2 wei, to buy the token ID 1.
Call (the parent ERC721) function ownerOf(1) to validate that the owner is now the second address.
If you let anyone call the approve function, it would allow anyone to approve themselves to take NFTs! The purpose of approve is to give the owner of an asset the ability to give someone else permission to transfer that asset as if it was theirs.
The basic premise of any sale is that you want to make sure that you get paid, and that the buyer receives the goods in return for the sale. Petr Hedja's solution takes care of this by having the buy function not only transfer the NFT, but also include the logic for sending the price of the token. I'd like to recommend a similar structure with a few changes. One is so that the function will also work with ERC20 tokens, the other is to prevent an edge case where if gas runs out during execution, the buyer could end up with their NFT for free. This is building on his answer, though, and freely uses some of the code in that answer for architecture.
Ether can still be set as the accepted currency by inputting the zero address (address(0)) as the contract address of the token.
If the sale is in an ERC20 token, the buyer will need to approve the NFT contract to spend the amount of the sale since the contract will be pulling the funds from the buyer's account directly.
pragma solidity ^0.8.4;
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';
contract MyToken is ERC721 {
event NftBought(address _seller, address _buyer, uint256 _price);
mapping (uint256 => uint256) public tokenIdToPrice;
mapping (uint256 => address) public tokenIdToTokenAddress;
constructor() ERC721('MyToken', 'MyT') {
_mint(msg.sender, 1);
}
function setPrice(uint256 _tokenId, uint256 _price, address _tokenAddress) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
tokenIdToPrice[_tokenId] = _price;
tokenIdToTokenAddress[_tokenId] = _tokenAddress;
}
function allowBuy(uint256 _tokenId, uint256 _price) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
require(_price > 0, 'Price zero');
tokenIdToPrice[_tokenId] = _price;
}
function disallowBuy(uint256 _tokenId) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
tokenIdToPrice[_tokenId] = 0;
}
function buy(uint256 _tokenId) external payable {
uint256 price = tokenIdToPrice[_tokenId];
require(price > 0, 'This token is not for sale');
require(msg.value == price, 'Incorrect value');
address seller = ownerOf(_tokenId);
address tokenAddress = tokenIdToTokenAddress[_tokenId];
if(address != address(0){
IERC20 tokenContract = IERC20(tokenAddress);
require(tokenContract.transferFrom(msg.sender, address(this), price),
"buy: payment failed");
} else {
payable(seller).transfer(msg.value);
}
_transfer(seller, msg.sender, _tokenId);
tokenIdToPrice[_tokenId] = 0;
emit NftBought(seller, msg.sender, msg.value);
}
}
// mapping is for fast lookup. the longer operation, the more gas
mapping(uint => NftItem) private _idToNftItem;
function buyNft(uint tokenId) public payable{
uint price=_idToNftItem[tokenId].price;
// this is set in erc721 contract
// Since contracts are inheriting, I want to make sure I use this method in ERC721
address owner=ERC721.ownerOf(tokenId);
require(msg.sender!=owner,"You already own this nft");
require(msg.value==price,"Please submit the asking price");
// since this is purchased, it is not for sale anymore
_idToNftItem[tokenId].isListed=false;
_listedItems.decrement();
// this is defined in ERC721
// this already sets owner _owners[tokenId] = msg.sender;
_transfer(owner,msg.sender,tokenId);
payable(owner).transfer(msg.value);
}
this is Nft struct
struct NftItem{
uint tokenId;
uint price;
// creator and owner are not same. creator someone who minted. creator does not change
address creator;
bool isListed;
}
I want to send erc20 tokens to contract address which is able to trade the token.
However, I failed the test and error says
Error: VM Exception while processing transaction: revert
My function is this
TokenSale.sol
function startSale(address _tokenSaleContractAddress) public {
require(msg.sender == admin);
require(tokenContract.transfer(_tokenSaleContractAddress, 750000));
}
MyToken.sol
function transfer(address _to, uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value);
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
My test is this
it('facilitates start sale', function() {
return MyToken.deployed().then(function(instance) {
tokenInstance = instance;
return TokenSale.deployed()
}).then(function(instance) {
tokenSaleInstance = instance;
return tokenSaleInstance.startSale(tokenSaleInstance.address, {from: admin} )
}).then(function(receipt) {
return tokenInstance.balanceOf(tokenSaleInstance.address)
}).then(function(balance) {
assert.equal(balance.toNumber, 750000);
});
});
Could you give me any advise why I failed the test?
Admin has 1000000 tokens, and I want to send 750000 tokens to TokenSale contract.
To do this, the admin needs to call transfer on the token contract. I.e. your test code should have this in it:
tokenInstance.transfer(tokenSaleInstance.address, 750000, { from: admin });
After that, calling startSale should succeed, but there's no reason to call it because it's just transferring 750,000 tokens to itself.