I am creating a Dapp for ERC721 NFTs. To execute a sale of an NFT from one user to another on my smart contract, I am calling the _safeTransfer function of openzeppelin from within another external function (called from my front-end). In exchange of the NFT, Ethers will need to be transferred from the purchaser to the seller using seller.transfer(msg.value). Furthermore, a commission will need to be sent to the smart contract itself.

My question is, how should I execute these three transactions (_safeTransfer, seller.transfer(msg.value) and send Ether to the contract) on my smart contract? From my understanding, if I initiate a "send" call from my Dapp, all the funds would go to the smart contract? Is this correct? If this is correct, I assume that I should be sending the funds to the seller from the smart contract after executing the _safeTransfer function? Is this how it is typically done?

Also, how can I revert ALL the transactions if the NFT or Ether transfer to the seller fails?

Thank you. J

  • too many questions. test that scenario by yourself using remix. if a transaction revert... usually every change that transaction did will be reverted – Majd TL Dec 6 '20 at 11:02

There are many ways to achieve that. One quick & dirty example is as follows.

Note: The code doesn't compile is just to give you some ideas.

contract TokenMkt {
    struct TokenInfo {
        address payable owner;
        uint256 price;

    ERC721 nftToken;

    // Info for tokens on sale
    mapping (uint256 => TokenInfo) forSale;

    uint256 constant COMMISION_FEE = 2; // Commission fee

    // Must be called to put a token on sale (price is in ether)
    function putOnSale(uint256 tokenId, uint256 price) public {

        // No error checking for owner
        TokenInfo storage ti = forSale[tokenId]
        ti.owner = msg.sender;
        ti.price = price;

        // Transfer token's ownership to the contract
        nftToken.safeTransferFrom(owner, address(this), tokenId);

    // Buy a token on sale
    function buyToken(uint256 tokenId) public payable {

        // Missing check if tokenId is valid
        TokenInfo storage ti = forSale[tokenId]

        // Check ether sent is enough
        require(msg.value >= ti.price, "Not enough ether");

        // Transfer token to buyer
        nftToken.safeTransferFrom(address(this), msg.sender, tokenId);

        address payable owner = ti.owner;
        uint256 fee = ti.price * COMMISION_FEE / 100;
        walletFee.transfer(fee); // Accumulate commission fees

        owner.transfer(ti.price - fee); // Send paid amount to previous owner

        // Return excess to buyer
        msg.sender.transfer(msg.value - ti.price);

Note2: If one of the address.transfer(amount) fails it reverts the whole transaction.

Note3: After last fork some guides do not recommend to use address.transfer() to send ether. Some contract with complex fallbacks stopped working because after the fork a few opcodes were more expensive and the fallback used more than the 2300 gas stipend.

  • Thank you very much Ismael for your detailed answer! – JF0001 Dec 7 '20 at 2:31

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