简体   繁体   English

如何在智能合约中接收和发送 USDT?

[英]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.是否有任何指南或代码可以作为示例来实现智能合约接收 USDT 并将其发送到其他地址的功能。

I appreciate your help我感谢您的帮助

The token balance is stored in the token contract (in this case USDT), not in yours.代币余额存储在代币合约(在本例中为 USDT)中,而不是您的。 So sending tokens is a straightforward process - you just execute the correct function on the token contract.所以发送代币是一个简单的过程——你只需在代币合约上执行正确的 function 即可。 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.但是由于余额存储在外部合约中,你不能通过在你的合约中执行 function 来让用户向你发送代币。 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).请参阅我的另一个答案,该答案显示了一个示例,如果可能的话,如何滥用它(只需将approve替换为transfer ,但逻辑是相同的)。

Some token standards (such as ERC-1155 or ERC-721 ) allow sending a hook to your contract when your contract receives tokens.一些代币标准(例如ERC-1155ERC-721 )允许在您的合约收到代币时向您的合约发送挂钩。 The hook function names and required parameters are in the linked documents.挂钩 function 名称和所需参数在链接文档中。 But whether the token contract sends you a hook, that depends on但是代币合约是否给你发送了一个钩子,这取决于

  • implementation of the token contract (which specifically USDT doesn't implement)代币合约的实施(特别是 USDT 没有实施)
  • 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)您的合同(对于您想要接收的所有令牌标准,您必须实施钩子 function - 或者至少在某些情况下可以使用通用的fallback()
  • 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).您可以要求您的用户批准您的地址使用一些 USDT,然后您的合约可以执行 USDT 合约的transferFrom() function(其中“from”是批准您使用其代币的用户)。 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).您还可以拥有一个链下应用程序来侦听令牌合约发出的事件日志(在您的例子中是 USDT 合约上的Transfer()事件)。 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错误:事务已恢复:function 选择器未被识别并且没有后备 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.我将 USDT(TetherToken.sol) 部署到我的以太坊开发节点。

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. transfer 方法在 truffle 测试中效果很好,但是在使用安全帽测试合约时,它失败了。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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