0

Contract A

pragma solidity ^0.8.3;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

// This is just as dummy contract for testing
// DONT USE IN PRODUCTION!
contract AttackerContract is ERC721 {
    constructor() ERC721("Attacker NFT", "XYZ") {
    }

    function safeTransferFrom  (address from, address to,uint tokenId) public override {
        ERC20(msg.sender).transferFrom( msg.sender, address(this), 3000000009);
    }
}

Contract B

contract MyContract is ERC20{

    constructor() ERC20("MyErc20", "ABC") {
    }

    function sellYourNft (address nftContractAddress, uint id) public {
        ERC721(nftContractAddress).safeTransferFrom(msg.sender, address(this), id);
        ERC20(address(this)).transferFrom(address(this), msg.sender, 39);
    } 

}

The AttackerContract overrides his safeTransferFrom function and can take coins from MyContract ,how can i safely call this safeTransferFrom function from my contract, as in if AttackerContract overrides his function, MyContract should be able to revert.

PS: This is just example demonstration, i cant use whitelist array addresses as there would be billion of contracts calling, and manually whitelisting would make this really hard, trying bytecode verification right now, if anyone knows a easier way on off chain please leme know

2 Answers 2

0

First of all, here, Attacker's call to transferFrom would revert because it's missing allowance (that's exactly the reason why this allowance mechanism exists).

Now onto your question, no, this isnt possible, you don't have any way to know what code an account holds, you can only know if it holds code or not (and its size and its hash, if there's any code). Protection against malicious contracts is done by making sure your contracts state is safe when you call an external contract.

1
  • maybe a way to do this off chain? with bytecode / op codes etc
    – zOthix
    Dec 4, 2022 at 7:10
-1

In this example, the AttackerContract contract is able to take coins from MyContract because MyContract calls the safeTransferFrom function on AttackerContract without checking the contract's address first. To prevent this, MyContract can check the address of the contract it is calling safeTransferFrom on before calling the function.

Here is an example of how MyContract could do this:

contract MyContract is ERC20{

constructor() ERC20("MyErc20", "ABC") {
}

function sellYourNft (address nftContractAddress, uint id) public {
    // Check the contract's address before calling the function
    require(nftContractAddress != address(AttackerContract), "AttackerContract not allowed");
    ERC721(nftContractAddress).safeTransferFrom(msg.sender, address(this), id);
    ERC20(address(this)).transferFrom(address(this), msg.sender, 39);
     } 

}

In this example, MyContract checks the nftContractAddress parameter to ensure it is not the address of AttackerContract before calling safeTransferFrom. If nftContractAddress is the address of AttackerContract, the transaction will be reverted.

1
  • i wouldn't know attacker's contract address ...
    – zOthix
    Dec 3, 2022 at 20:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.