How my contract can transfer some tokens in another 3rd party contract?

I want get payments from users in USDT to my USDT wallet and mint them token of my contract with rate 1:1. Now I wrote something like that:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "hardhat/console.sol";

contract Question is ERC20 {
    address USDTAddress;
    address BoxAddress;

    constructor(address _USDTAddress) ERC20("Question", "QUE") {
        USDTAddress = _USDTAddress;
        BoxAddress = msg.sender;
        _mint(msg.sender, 10000000000000000000000000);
    }

    function depositUSDT(uint _tokenAmount) public payable {
        ERC20 USDTToken = ERC20(USDTAddress);

        USDTToken.approve(USDTToken.tokenHolder, _tokenAmount);

        USDTToken.transferFrom(msg.sender, BoxAddress, _tokenAmount);
        _mint(msg.sender, _tokenAmount);
    }
}

But my unit tests not passed...

Please, help!

I think you should delete this line, users need to call USDT.approve(Question_Contract_address, Approval_Amount) at first, and then they can call depositUSDT().

And it seems like the decimals of your QUE token is 18, but the decimals of USDT is 6, maybe you should make some changes about this.

This is the contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "hardhat/console.sol";

contract Question is ERC20 {
    address USDTAddress;
    address BoxAddress;

    IERC20 public USDTToken;
    IERC20 public MXToken;

    constructor(address _USDTAddress, address _BoxAddress) ERC20("Question", "QUE") {
        USDTAddress = _USDTAddress;

//        console.log(USDTAddress); // tokenUSDT.address

//        console.log(msg.sender); // unknown
//        console.log(_BoxAddress); // commonWallet.address

        BoxAddress = _BoxAddress; // equal to commonWallet.address
        MXToken = ERC20(address(this));

//        console.log(address(this)); // tokenMatrix.address

        USDTToken = ERC20(USDTAddress);
        _mint(BoxAddress, 1000000000000000000000);
    }

    function depositUSDT(uint256 _amount) payable public {

//        console.log(msg.sender); // walletUser.address
//        console.log(_amount); // ok

        USDTToken.approve(msg.sender, _amount);

        uint temp = USDTToken.balanceOf(msg.sender);
        console.log(temp);

        USDTToken.transferFrom(msg.sender, BoxAddress, _amount);

//        MXToken.approve(BoxAddress, _amount);
//        MXToken.transferFrom(BoxAddress, msg.sender, _amount);

//        USDTToken.approve(msg.sender, BoxAddress, _amount);
//        USDTToken.transferFrom(msg.sender, BoxAddress, _amount);

//        _mint(msg.sender, _amount);
    }
}

This is unit test:

const { expect } = require("chai")
const { ethers, waffle } = require('hardhat')
const { deployContract } = waffle

// contracts
const QJson = require('../artifacts/contracts/Question.sol/Question.json')
const MockUSDT = require('../artifacts/contracts/mocks/mockUSDT.sol/MockUSDT.json')

describe('Question.sol', _ => {
  it('Deposit USDT with Question.sol', async () => {
    const [deployerUSDT, deployerMatrix, commonWallet, walletUser] = await ethers.getSigners()

    const tokenUSDT = await deployContract(deployerUSDT, MockUSDT)

    const tokenMatrix = await deployContract(deployerMatrix, QJson, [
      tokenUSDT.address,
      commonWallet.address,
    ])

    await tokenUSDT.connect(deployerUSDT).transfer(walletUser.address, 11)
    let balance = await tokenUSDT.balanceOf(walletUser.address)
    expect(balance).to.equal(11) // ok

    // await tokenUSDT.approve(walletUser.address, 9)
    await tokenMatrix.connect(walletUser).depositUSDT(9)
    let usdtBalance = await tokenUSDT.balanceOf(walletUser.address)
    console.log(usdtBalance)

    // let matrixBalance = await tokenMatrix.balanceOf(walletUser.address)
    // console.log(matrixBalance, usdtBalance)

    // // expect(matrixBalance).to.equal(amount2)
  }).timeout(20000)
})

And I've got errror: ERC20: transfer amount exceeds allowance

I think you should delete this line.

And in your script, you can write like this:

await tokenUSDT.connect(walletUser).approve(tokenMatrix.address, AMOUNT);
await tokenMatrix.connect(walletUser).depositUSDT(9)
1 Like

Thank you very much! It is it!

1 Like

Sorry, now I try create same method but for withdraw back USDT:

    function withdrawUSDT(uint _amount) payable public {
//        USDTToken.approve(BoxAddress, _amount);
        USDTToken.transferFrom(BoxAddress, msg.sender, _amount);
        _burn(msg.sender, _amount);
    }

And test:

  it('Deposit and withdraw USDT', async () => {

    const {
      deployerUSDT,
      commonWallet,
      userWallet,
      tokenUSDT,
      tokenMatrix,
    } = await prepare()

    await tokenUSDT.connect(deployerUSDT).transfer(userWallet.address, 11)
    let balance = await tokenUSDT.balanceOf(userWallet.address)
    expect(balance).to.equal(11) // ok

    await tokenUSDT.connect(userWallet).approve(tokenMatrix.address, 9)
    await tokenMatrix.connect(userWallet).depositUSDT(9)

    let usdtBalance = await tokenUSDT.balanceOf(userWallet.address)
    expect(usdtBalance).to.equal(2)

    let mxTokenBalance = await tokenUSDT.balanceOf(commonWallet.address)
    expect(mxTokenBalance).to.equal(9)

    let mxUserBalance = await tokenMatrix.balanceOf(userWallet.address)
    expect(mxUserBalance).to.equal(9)

    // await tokenUSDT.connect(userWallet).approve(tokenMatrix.address, 9)
    // await tokenMatrix.connect(userWallet).depositUSDT(9)

// up to here everything is fine!

    await tokenMatrix.connect(userWallet).approve(userWallet.address, 9)
    await tokenMatrix.connect(userWallet).withdrawUSDT(9)

// there I've got the error...

  }).timeout(20000)

But I've got same error: ERC20: transfer amount exceeds allowance

Can you help me?

This is full contract and test code:
Contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "hardhat/console.sol";

contract Matrix is ERC20 {
    address USDTAddress;
    address BUSDAddress;
    address BoxAddress;

    IERC20 public USDTToken;
    IERC20 public BUSDToken;
    IERC20 public MXToken;

    constructor(address _BoxAddress, address _USDTAddress, address _BUSDAddress) ERC20("Matrix", "XUSD") {
        BoxAddress = _BoxAddress;
        USDTAddress = _USDTAddress;
        BUSDAddress = _BUSDAddress;
        MXToken = ERC20(address(this));
        USDTToken = ERC20(USDTAddress);
        BUSDToken = ERC20(BUSDAddress);
    }

    function depositUSDT(uint _amount) payable public {
        USDTToken.transferFrom(msg.sender, BoxAddress, _amount);
        _mint(msg.sender, _amount);
    }

    function withdrawUSDT(uint _amount) payable public {
//        USDTToken.approve(BoxAddress, _amount);
        USDTToken.transferFrom(BoxAddress, msg.sender, _amount);
        _burn(msg.sender, _amount);
    }

    function depositBUSD(uint _amount) payable public {
        BUSDToken.transferFrom(msg.sender, BoxAddress, _amount);
        _mint(msg.sender, _amount);
    }

//    function withdrawBUSD(uint _amount) payable public {
//        BUSDToken.transferFrom(msg.sender, BoxAddress, _amount);
//        _mint(msg.sender, _amount);
//    }
}

Test:

const { expect } = require("chai")
const { ethers, waffle } = require('hardhat')
const { deployContract } = waffle

// contracts
const Matrix = require('../artifacts/contracts/Matrix.sol/Matrix.json')
const MockUSDT = require('../artifacts/contracts/mocks/mockUSDT.sol/MockUSDT.json')
const MockBUSD = require('../artifacts/contracts/mocks/mockBUSD.sol/MockBUSD.json')

// BUSD: https://testnet.bscscan.com/token/0xeD24FC36d5Ee211Ea25A80239Fb8C4Cfd80f12Ee
// USDT: https://testnet.bscscan.com/token/0x337610d27c682E347C9cD60BD4b3b107C9d34dDd
// BUSD and USDT all have 18 decimals

const prepare = async () => {
  const [
    deployerUSDT,
    deployerBUSD,
    deployerMatrix,
    commonWallet,
    userWallet
  ] = await ethers.getSigners()

  const tokenUSDT = await deployContract(deployerUSDT, MockUSDT)
  const tokenBUSD = await deployContract(deployerBUSD, MockBUSD)
  const tokenMatrix = await deployContract(deployerMatrix, Matrix, [
    commonWallet.address,
    tokenUSDT.address,
    tokenBUSD.address,
  ])

  return {
    deployerUSDT,
    deployerBUSD,
    deployerMatrix,
    commonWallet,
    userWallet,
    tokenUSDT,
    tokenBUSD,
    tokenMatrix,
  }
}

describe('Deposit/withdraw BUSD and USDT with Matrix.sol', _ => {

  it('Deposit and withdraw USDT', async () => {

    const {
      deployerUSDT,
      commonWallet,
      userWallet,
      tokenUSDT,
      tokenMatrix,
    } = await prepare()

    await tokenUSDT.connect(deployerUSDT).transfer(userWallet.address, 11)
    let balance = await tokenUSDT.balanceOf(userWallet.address)
    expect(balance).to.equal(11) // ok

    await tokenUSDT.connect(userWallet).approve(tokenMatrix.address, 9)
    await tokenMatrix.connect(userWallet).depositUSDT(9)

    let usdtBalance = await tokenUSDT.balanceOf(userWallet.address)
    expect(usdtBalance).to.equal(2)

    let mxTokenBalance = await tokenUSDT.balanceOf(commonWallet.address)
    expect(mxTokenBalance).to.equal(9)

    let mxUserBalance = await tokenMatrix.balanceOf(userWallet.address)
    expect(mxUserBalance).to.equal(9)

    // await tokenUSDT.connect(userWallet).approve(tokenMatrix.address, 9)
    // await tokenMatrix.connect(userWallet).depositUSDT(9)
    await tokenMatrix.connect(userWallet).approve(userWallet.address, 9)
    await tokenMatrix.connect(userWallet).withdrawUSDT(9)

  }).timeout(20000)

  it('Deposit and withdraw BUSD', async () => {
    const {
      deployerBUSD,
      commonWallet,
      userWallet,
      tokenBUSD,
      tokenMatrix,
    } = await prepare()

    await tokenBUSD.connect(deployerBUSD).transfer(userWallet.address, 11)
    let balance = await tokenBUSD.balanceOf(userWallet.address)
    expect(balance).to.equal(11) // ok

    await tokenBUSD.connect(userWallet).approve(tokenMatrix.address, 9)
    await tokenMatrix.connect(userWallet).depositBUSD(9)

    let busdBalance = await tokenBUSD.balanceOf(userWallet.address)
    expect(busdBalance).to.equal(2)

    let mxTokenBalance = await tokenBUSD.balanceOf(commonWallet.address)
    expect(mxTokenBalance).to.equal(9)

    let mxUserBalance = await tokenMatrix.balanceOf(userWallet.address)
    expect(mxUserBalance).to.equal(9)
  }).timeout(20000)
})

Maybe the above code should be changed to:

await tokenUSDT.connect(BoxAddress).approve(tokenMatrix.address, 9)
await tokenMatrix.connect(userWallet).withdrawUSDT(9)

Can I then represent this test logic in the real world?
Because the user logs in through his wallet (metamask), from which he paid ...
tokenMatrix.connect is like a connecting metamask wallet?

Although it can work as you expect, but I think you should test more to use.