1

I wrote a contract in Solidity with the following task in mind:

  • The user approves the smart contract to transfer a certain amount of tokens from their wallet.

  • The smart contract uses this amount to transfer to an address given as a parameter. But it also takes 1 token from this amount to transfer to the developer of the contract.

  • If everything succeeds, the user receives a "Payment successful!" message.

The first step (the approval) was made using React and Web3:

const derc20contract = '0x36850b80ad73fe66216609B9796ed6ceae8BE29F';
const handleClick = async (e) => {
            e.preventDefault();
            const prtmp = await detectEthereumProvider();
            
            // -------------------- Approve Part ------------------------------// 
            const web3 = new Web3(prtmp);
            const erc20contract = new web3.eth.Contract(
                erc20abi, 
                derc20token,
                { from: '0xFromAddress' }
            );

            await erc20contract.methods.approve(derc20contract, web3.utils.toHex(3e18)).send();
            // ---------------------------------------------------------------//

            const contract = new web3.eth.Contract(derc20abi, derc20contract);
            const response = await contract.methods.send_usdt(
                '0xToAddress',
                web3.utils.toHex(3e18)
            )
            .call({ from: '0xFromAddress'});
            console.log(response);
    };



Once the approval succeeds the second part of the function resolves. The contract I deployed has a function called send_usdt. Through this function is my smart contract able to transfer the amount approved.

/ SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

interface IERC20 {
    function transfer(address _to, uint256 _value) external returns(bool);
    function transferFrom(address _from, address _to, uint _amount) external returns (bool);
    function allowance(address _owner, address _contract) external returns (uint256);
    function balanceOf(address _buyer) external view returns (uint256);
    function approve(address _contract,  uint tokens) external returns (bool);
}

contract DPortalSubscription {

    address private owner;
    mapping(address => uint32) subscription_fees;
    
    constructor()
    { 
        owner = msg.sender; 
    }

    function check_balance() external view returns (uint256)
    {
        // TestToken ( Plasma Bridge ) in Mumbai Network 
        IERC20 usdt = IERC20(address(0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1));
        uint256 balance = usdt.balanceOf(msg.sender);
        return balance;
    }

    function check_sender_address() external view returns (address)
    {
        return msg.sender;
    }

    function check_allowance()external returns(uint256)
    {
        // TestToken ( Plasma Bridge ) in Mumbai Network 
        IERC20 usdt = IERC20(address(0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1));
        uint256 allowance = usdt.allowance(msg.sender, address(this));
        return allowance;
    }

    function send_usdt(address _to, uint256 _amount) external returns (string memory)
    {
        // TestToken ( Plasma Bridge ) in Mumbai Network 
        IERC20 usdt = IERC20(address(0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1));
        require(_amount > 1, "Purchases must be higher than 1 usdt");
        require(usdt.balanceOf(msg.sender) >= _amount, "The buyer doesn't have enough funds");
        require(usdt.allowance(msg.sender, address(this)) >= _amount, "Permissions to transfer tokens denied");
        require(usdt.transferFrom(msg.sender, _to, _amount-1) == true, "Couldn't transfer tokens to seller");
        require(usdt.transferFrom(msg.sender, owner, 1) == true, "Couldn't transfer tokens to support");
        return "Payment successful!";  
    }
}

Once the second part of the function in React resolves, I receive the confirmation "Payment successful!" But the tokens weren't transferred. I keep the same amount in my "From Wallet", "To Wallet" and "Deploy Wallet" as before.


The problem was inside the react snippet. By changing the method call to sell the money could be successfully transferred.

const response = await contract.methods.send_usdt
(
    '0xToAddress',
    web3.utils.toHex(3e18)
)
.call({ from: '0xFromAddress' });

was changed to:

const response = await contract.methods.send_usdt
(
    '0xToAddress',
    web3.utils.toHex(3e18),
    { from: '0xFromAddress' }
)
.send();

which worked but raised another question. When I use the previous call method, the function returns "Payment Successful!" and with the sell method I receive an Object.

Since "Payment Successful" isn't true, I was wondering if it could be possible to throw an Exception when someone interacts with my contract using the call method.

Is that possible?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Javi
  • 461
  • 6
  • 11
  • Please read "[ask]" and "[answer]" and their linked pages. Please don't summarize solutions in your question. SO pages are separated into a single question section, followed by one or more answers. Don't put solutions in your question. SO isn't a message board, it's more like an online Q/A reference or a cookbook. As such, questions and answers are separate and have to be kept separate. See https://meta.stackoverflow.com/a/271399/128421 and https://meta.stackoverflow.com/q/267434/128421. – the Tin Man Feb 07 '22 at 05:40
  • In addition, please don't use "edit" or "update" tags to mark changes. Simple update the text with the new information as if it'd been there from the beginning. "[Should “Edit:” in edits be discouraged?](https://meta.stackoverflow.com/q/255644/128421)" helps explain. – the Tin Man Feb 07 '22 at 05:42

1 Answers1

0

Following @Kuly14's answer and in order to make it clearer, you may want to create the following function instead:

function send_usdt(address _to, uint256 _amount) external returns (string memory) {
    // TestToken ( Plasma Bridge ) in Mumbai Network 
    IERC20 usdt = IERC20(address(0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1));
    require(_amount > 1, "Purchases must be higher than 1 usdt");

    usdt.transferFrom(msg.sender, owner, 1);
    usdt.transferFrom(msg.sender, _to, _amount-1);

    return "Payment successful!";  
}

Notes:

  1. By just calling the transferFrom method, it will jump its corresponding error if something goes wrong. You do not need to handle them.
  2. I have changed the order in which you transfer the tokens. First, I send them to the owner and then to the desired recipient to avoid fees payments problems, but if you prefer the other way round, it is possible.
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
NuMa
  • 588
  • 2
  • 7
  • Thank you very much NuMa. I changed my code the way you proposed :) I tried the solution from you and Kuly14. Sadly it didn't work. BUT what worked was changing in my react snippet the method 'call' to 'sell'. I will post the solution – Javi Feb 05 '22 at 10:23