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.
How can I send tokens to a token holders from inside a smart contract with solidity?
It means how can send reward to token holders?
Have a list of addresses and loop through them while calling native erc transfer method. You can't really iterate through a mapping without knowing the access keys (if you're thinking about pulling addresses from smth like balances).
I am assuming you want to send Ether to another Smart Contract or an EOA (e.g. Metamask). You can write a Smart Contract such as the below and use the Remix Ethereum (an IDE) to send Ether to an external party. Use the public function - transferEther.
//SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.6.0 < 0.9.0;
contract Sample{
address public owner;
constructor() {
owner = msg.sender;
}
receive() external payable {
}
fallback() external payable {
}
function getBalance() public view returns (uint){
return address(this).balance;
}
// this function is for sending the wei/ether OUT from this smart contract (address) to another contract/external account.
function transferEther(address payable recipient, uint amount) public returns(bool){
require(owner == msg.sender, "transfer failed because you are not the owner."); //
if (amount <= getBalance()) {
recipient.transfer(amount);
return true;
} else {
return false;
}
}
}
I've got this piece of code:
function mint(uint amount) public payable {
require(amount <= 10, "token: max of 10 token per mint");
require(_openMint == true, "token: minting is closed");
require(msg.value == _price*amount, "token: must send correct price");
require(_tokenIdTracker.current() + amount <= _max, "token: not enough token left to be mint amount");
for(uint i = 0; i < amount; i++) {
_mint(msg.sender, _tokenIdTracker.current());
_tokenIdTracker.increment();
}
IERC20("0x0789fF5bA37f72ABC4D561D00648ac0000970000").safeTransferFrom(msg.sender, owner(), amount);
}
It mints an ERC721 token with payment in another ERC20 token, and i need to know how muchy ERC20 is being sent. Is there any way to do this?
if you want to know the token amount sent on every transaction, you only have to go on etherscan, search for the contract address, and click the transaction hash of "mint"
if you want to know all amount that was sent, you have to use some tools like thegraph.com
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 used the following code which I thought would be able to withdraw any ether that was deposited to the smart contract. Can you help explain why this might not work?
function withdraw() public {
require(owner == msg.sender);
msg.sender.transfer(address(this).balance);
}
I am having issues withdrawing the ether and was wondering which part of the code is preventing me from doing so.
function withdraw(uint amount) public payable{
require(address(this).balance >= amount);
msg.sender.transfer(amount);
}
use this code