How to receive and send USDT in a smart contract?
Asked Answered
E

2

13

Is there any guide or code that can serve as an example to implement the functionality where a smart contract receives and sends USDT to other addresses.

I appreciate your help

Ewan answered 12/5, 2021 at 22:22 Comment(0)
P
13

The token balance is stored in the token contract (in this case USDT), not in yours. So sending tokens is a straightforward process - you just execute the correct function on the token contract. Mind that your contract needs to hold at least the amount it's about to send, otherwise the transaction would revert.

pragma solidity ^0.8;

interface IERC20 {
    function transfer(address _to, uint256 _value) external returns (bool);
    
    // don't need to define other functions, only using `transfer()` in this case
}

contract MyContract {
    // Do not use in production
    // This function can be executed by anyone
    function sendUSDT(address _to, uint256 _amount) external {
         // This is the mainnet USDT contract address
         // Using on other networks (rinkeby, local, ...) would fail
         //  - there's no contract on this address on other networks
        IERC20 usdt = IERC20(address(0xdAC17F958D2ee523a2206206994597C13D831ec7));
        
        // transfers USDT that belong to your contract to the specified address
        usdt.transfer(_to, _amount);
    }
}

But because the balance is stored in the external contract, you can't just make a user send you tokens by executing a function in your contract. See my other answer that shows an example how this could be misused if it were possible (just replace approve to transfer, but the logic is the same).

Some token standards (such as ERC-1155 or ERC-721) allow sending a hook to your contract when your contract receives tokens. The hook function names and required parameters are in the linked documents. But whether the token contract sends you a hook, that depends on

  • implementation of the token contract (which specifically USDT doesn't implement)
  • your contract (you would have to implement the hook function for all token standards that you want to receive - or at least a generic fallback() which would work in some cases)
  • and sometimes on the sender as well.

You can ask your users to approve some USDT to be spent by your address, and then your contract can execute the transferFrom() function of the USDT contract (where the "from" is the user who approved you to spend their tokens). However, the approval needs to be done outside of your contract, as the linked other answer suggests.

You can also have an off-chain app that listens to event logs emitted by the token contract (in your case Transfer() event on the USDT contract). The event logs contain the transfer information including the receiver and amount. So your (off-chain) app can filter only events where your address is the receiver, and perform some action when it handles the event log.

Pretrice answered 13/5, 2021 at 0:43 Comment(17)
hi Petr, thanks for the help. Trying to execute the code, I have an error and it is the following: "ParserError: Only state variables or file-level variables can have a docstring." This error about the variable "usdt"Ewan
Oh I used the multi-line comment block to split the comment for better readability in an IDE that doesn't check docblocks by default (Remix). If you remove the comment, the error will disappear.Pretrice
hi Petr, but if I comment the line of the error the problem would pass, but then in what way can I send fundsEwan
I meant remove the multi-line comment starting with /** and ending with */ - or replace it with single-line comments like I just did in the question.Pretrice
" - there's no contract on this address on other networks" Is it possible to use usdt tx in testnet's ?Shamikashamma
@NikolaLukic There's no official USDT contract on testnets, so keep in mind that you need to use a copy of it that is deployed on a different address.Pretrice
@PetrHejda ANy suggest : #71604399Shamikashamma
@PetrHejda If we want to use USDT token when deploying smart contract payment, do we use Transfer or transferFrom ? and do we need to get the approval from the address of the USDT in order to use USDT as payment? thanksGroningen
@DavidJay You can use transfer() from your contract only if you're transferring tokens from your contract address. If you want to transfer tokens from the user address, they need to approve() your contract as spender first, and then you can use transferFrom().Pretrice
If I understand well, with an ERC20 token using a real USDT address inside a payment function we need to implement approve then transferFrom. So Basically this line is not enough "token.transferFrom(subscriber, plan.merchant, plan.amount)" even by using ERC20 token like USDT or USDC. please check the pay function through this link ethereum.stackexchange.com/questions/127865/…. Do you think in my pay function I need to approve first?Groningen
@DavidJay The transferFrom() is sufficient on the side of your contract. However, before executing this function, the subscriber also needs to execute approve(yourContract, amount) on the token contract directly (not through your contract as an intemediary).Pretrice
@PetrHejda for safety I wanted to use safeTransferFrom function from safeERC20 instead of transferFrom in this the smart contract I shared with you above.I Imported safeERC20 and changed "token.transferFrom(subscriber, plan.merchant, plan.amount)" with safeTransferFrom(token, subscriber, plan.merchant, plan.amount) and I got an error("Member "safeTransferFrom" not found or not visible after argument-dependent lookup in contract IERC20"). Am I implementing safeTransferFrom wrong ?Groningen
@DavidJay It seems that your token variable is of type IERC20 that does not define the safeTransferFrom() function... You need to change your token variable to type safeERC20 that defines this function.Pretrice
@PetrHejda When you say that the subscriber also needs to execute approve(yourContract, amount) on the token contract directly (not through your contract as an intemediary). do you mean that I should include approve(address(this), amount) inside pay function before excuting transform from ? Thanks a lotGroningen
@DavidJay I'm not sure what pay function you mean, however the user needs to invoke the approve() function directly - so for example through your UI. Not through your contract.Pretrice
If I understand well I should include token.approve(address(this), amout) inside the smart contract but in the UI I should called it first I use await for this one to be confirmed then token.transferFrom will be triggered? I shared a post that describes my error in detail if you could help. thanks a lot @PetrHejda ethereum.stackexchange.com/questions/130290/…Groningen
@DavidJay Do not invoke the token.approve() from your smart contract - only from the UI. In other words, the approve() function needs to be executed by the user directly, not through your contract.Pretrice
C
1

When I use the code above, I got an error

Error: Transaction reverted: function selector was not recognized and there's no fallback function

and I have no idea why

pragma solidity ^0.8.0;

import "hardhat/console.sol";

interface IERC20 {
    function transfer(address _to, uint256 _value) external returns (bool);
}

contract Greeter {

  string greeting;

  constructor(string memory _greeting) {
    console.log("Deploying a Greeter with greeting:", _greeting);
    greeting = _greeting;
  }

  function sendUSDT(address _to, uint256 _amount) external {
         // This is the mainnet USDT contract address
         // Using on other networks (rinkeby, local, ...) would fail
         //  - there's no contract on this address on other networks
    IERC20 usdt = IERC20(address(0x5FbDB2315678afecb367f032d93F642f64180aa3));
        
        // transfers USDT that belong to your contract to the specified address
    usdt.transfer(_to, _amount);
  }
}

I deployed the USDT(TetherToken.sol) to my ethereum dev node.

const TetherToken = artifacts.require("TetherToken"); 

contract('TetherToken',accounts => {
    before(async () => {
        let tetherToken = await TetherToken.at("0x5FbDB2315678afecb367f032d93F642f64180aa3");
        //this address is the same as signers[1].address in hardhat
        tetherToken.transfer("0x70997970c51812dc3a010c7d01b50e0d17dc79c8", web3.utils.toBN("1000000000"));
        let b = await tetherToken.balanceOf("0x70997970c51812dc3a010c7d01b50e0d17dc79c8")
        console.log(b.toString());
    });

});

The transfer method works pretty good with truffle test, but when test the contract with hardhat, it failed.

const { ethers, upgrades } = require("hardhat");

async function main() {

  const signers = await ethers.getSigners();

  const Greeter = await hre.ethers.getContractFactory("Greeter");
  const greeter = await Greeter.deploy("Hello, Hardhat!");

  await greeter.deployed();

  let overrides = {

    // The maximum units of gas for the transaction to use
    gasLimit: 2100000,

    // The price (in wei) per unit of gas
    gasPrice: ethers.utils.parseUnits('8.0', 'gwei')

  };

  await greeter.connect(signers[1]).sendUSDT(signers[2].address, ethers.utils.parseUnits('100.00', 'mwei'), overrides);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });
Certie answered 22/8, 2021 at 12:56 Comment(1)
If you are using USDT(BEP20), deploy to Binance Testnet. Otherwise if it is USDT(ERC20), deploy to Ethereum Testnet.Foulmouthed

© 2022 - 2024 — McMap. All rights reserved.