msg.sender is different in internal call - solidity

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.

Related

How to call a function from smart contract A on a token ID received from smart contract B?

I have two ERC721 smart contracts A and B. I have successfully minted a token ID on contract A and transferred it to contract B address (to transfer it to a contract address and not a wallet address I used IERC721Receiver). From here, is there a way for contract's B functions, which take a token ID as argument, to be called on the token ID received from A that now belongs to B?
For example, if Contract A was:
contract ContractA is ERC721 {
...
function mint(address _to, uint256 _mintAmount) public payable {
for (uint256 i = 1; i <= _mintAmount; i++) {
_safeMint(_to, supply + i);
}
}
}
and contract B was:
contract ContractB is ERC721 {
...
function exampleFunction(uint256 tokenId) public payable {
// Do something with tokenId
}
}
How can I call exampleFunction(6) on ContractB if token ID #6 was transferred from ContractA to ContractB (and not minted on ContractB)?
It just seems to me like there is no way to use methods from ERC721 contracts on token IDs that are not minted from the same contract where the methods are implemented.
Anything helps, so thank you in advance!
I am able to see that ContractB owns the token transferred to it by ContractA, but only on ContractA's ownerOf() method. I cannot do anything with that token on ContractB's methods, even though it now belongs to it.
You can call your exampleFunction() from your onERC721Received() implementation.
However you will not be able to do anything with the token as it will not have been transferred to you yet. The onERC721Received is purely to check that the contract supports receiving ERC721 tokens.

Foundry call smart contract from with EOA address

I am wtiting test case using Foundry. I want to call a custom smart contract function that changes the state of the smart contract called from a EOA so the msg.sender would be the EOA address. Here is my testing code:
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {console} from "forge-std/console.sol";
import {stdStorage, StdStorage, Test} from "forge-std/Test.sol";
import {Utils} from "./utils/Utils.sol";
import {MyERC20} from "../MyERC20.sol";
contract BaseSetup is MyERC20, DSTest {
Utils internal utils;
address payable[] internal users;
address internal alice;
address internal bob;
function setUp() public virtual {
utils = new Utils();
users = utils.createUsers(5);
alice = users[0];
vm.label(alice, "Alice");
bob = users[1];
vm.label(bob, "Bob");
}
function test() {
this.customFunction(); // want to call this function as Alice as the caller
}
}
So, in the code above, customFunction is defined on the MyERC20 contract, and it changes the smart contract state. I want to call the function with different EOA accounts like alice and bob. Is it possible, and if so, what it the syntax for that?
I would recommend using the prank cheatcode in foundry for this.
It's pretty straight forward.
interface CheatCodes {
function prank(address) external;
}
contract Test is DSTest {
CheatCodes cheatCodes;
function setUp() public {
cheatCodes = CheatCodes(HEVM_ADDRESS);
}
function test() public {
// address(1337) is now the caller of customFunction
cheatCodes.prank(address(1337));
address(contract).customFunction();
}
}
This pranked caller will only persist for a single call. Then you will have to instantiate the caller again with the prank cheatCode on future calls to the contract. Alternatively there is also a cheatCode called startPrank which will allow the custom caller to persist until stopPrank is called. Hope this helps!

ERC20 contract security

i try to create a withdraw function call by external, and i have a problem with erc20, when you look stoler function, i imagine somebody who create a contract with a contructor of on existing ERC20 and after he can take every token of the contract no? I hope no but when i try this each address can take Mytoken
Mytoken yourToken;
constructor(address tokenAddress) {
yourToken = Mytoken(tokenAddress);
}
function stoler() public{
yourToken.transfer(msg.sender, 1);
}
Yes, it's correct.
If you don't want that a users can withdraw the amount from the contract it's useful use the modifier.
The modifier can allow you to restrict the execution of function only a specific address. In this case you have two possibilities:
• Import 'Ownable' contract and use onlyOwner() modifier: in this way the function can be call only who user have deployed the smart contract in Web3.
If you want to use this function, you must to import the contract in this way:
import "#openzeppelin/contracts/access/Ownable.sol";
and you must update stoler() function in this mode:
function stoler() public onlyOwner {
yourToken.transfer(msg.sender, 1);
}
• Create a new modifier that allow a specific address to call the stolen() function and retrieve their token from the smart contract. In this case you must to define the modifier in this way:
modifier [NAMEMODIFIER] {
// your logic
_;
}
and then you must update stolen() function in this mode:
function stoler() public [NAMEMODIFIER] {
yourToken.transfer(msg.sender, 1);
}

Transfer ERC721 from owner to buyer

I'm trying to let other addresses execute the buy function, but it throws me the error ERC721: approve caller is not the owner nor approved for all
this is the code
function testbuy() public payable{
require(ownerOf(1) == _owner, "Already bought");
approve(msg.sender, 1);
safeTransferFrom(_owner, msg.sender, 1);
}
How could I make other address buy the NFT from the owner address? I'm having trouble understanding approve and setApproveForAll.. Thanks :)
Based on the error message, I'm assuming that you're using the Openzeppelin implementation of ERC721.
Since your contract derives from the OZ implementation, you can use their internal functions and don't need to go through the approve() and safeTransferFrom() process. If you wanted to go this way, you would need to invoke the approve() function from the _owner address directly - not by the buying user through the testbuy() function, as this was the logical error in your code.
Specifically, you can use the _transfer() function (source):
function testbuy() public payable{
require(ownerOf(1) == _owner, "Already bought");
_transfer(_owner, msg.sender, 1);
}
You can find your error OpenZeppelin ERC721 here:
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
Address which is calling testbuy() function has to be the owner of the token. In other case it cannot give the approval (or the token has to have approval for all), hence the approve function.

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

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