简体   繁体   中英

How to receive and send USDT in a smart contract?

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

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.

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);
  });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM