How to swap stablecoin with unlisted custom token without using Moralis plugins?

Ok so, weā€™re making some progress here, all I did was changing a line in the ABI.

From this :

stateMutability: "view"

To this :

stateMutability: "payable"

And now I got this little warning during the Metamask confirmation popup :

When it fails, I can finally see a transaction recorded on BscScan.

Let me try to see if I can find anything from the BscScan transaction.

No luck, even debug trace is not very helpful or descriptive about the error.

{
  "type": "CALL",
  "from": "0x04f...c19", // user wallet address
  "to": "0xa65...ac78", // smart contract address
  "value": "0x0",
  "gas": "0x1b28968",
  "gasUsed": "0xcb",
  "input": "0xfe029156000000000000000000000000337610d27c682e347c9cd60bd4b3b107c9d34ddd00000000000000000000000004fc654d2e22ef7243eb3cbc53d8aaf3fd25bc1900000000000000000000000000000000000000000000000012bc29d8eec7000000000000000000000000000000000000000000000000000029a2241af62c0000",
  "error": "execution reverted",
  "time": "0s"
}

Hereā€™s the transaction.

now it could be related to those approves
strange that you had to change that abi, didnā€™t you copy the abi from the contract compilation?

now it could be related to those approves

I thought so too. Probably because I was approving both transfers with the same Metamask wallet, when I shouldā€™ve approved the wallet that stores our custom token somewhere else.

Did some digging & found out about web3ā€™s signTransaction(), which can be used to automatically sign a transaction with apikey without the need to approve a transfer manually through Metamask.
So I created an api request that does that on the server side with nodejs.

...
const Web3 = require('web3');
const Accounts = require('web3-eth-accounts');
const Moralis = require('moralis/node');
...

require('dotenv').config();

const swapSmartCon = process.env.SWAP_SMART_CONTRACT;
const privateKey = process.env.TYS_PRIVATE_KEY;

router.post("/approve_tys_transfer", async (req, res) => {
  if(parseInt(req.body.buyerAmt) <= 0) {
    return false;
  }

  try {
    const accounts = new Accounts(process.env.BSC_SPEEDY_NODE); // TESTNET
    const buyerAmtStr = req.body.buyerAmt;
    const buyerAmt = Web3.utils.toWei(buyerAmtStr.toString(), 'ether');

    const params = {
      to: swapSmartCon, // swap smart contract address
      value: buyerAmt, // token value
      gas: 2000000,
      nonce: 0,
      chainId: parseInt(process.env.BSC_CHAIN_ID) // bsc testnet chain id
    };

    const signed = await accounts.signTransaction(params, privateKey);

    if(signed.hasOwnProperty("transactionHash")) {
      const json = {
        data: [signed.transactionHash],
        success: true,
        message: "Transaction approved."
      }

      res.json(json);
      res.status(200);
      return
    }

    console.log(signed);
  } catch (error) {
    console.log(error)
  }
})

With the above API method I was able to get the data back, with rawTransaction, transactionHash, etcā€¦

So what I did was:

  1. Have user to approve the transfer through the Metamask interface.
  2. Approve our wallet with the custom token by calling the API.
  3. Run the swap.

However, the transaction still failed but the error being thrown this time indicates that thereā€™s not enough funds in our wallet.

Hereā€™s the thing, thereā€™s enough custom token in the wallet. My guess is that signTransaction() is signing the wrong token maybe?

While I was looking through the transaction, I also noticed that the inputs were empty & the params should be passed through the constructor & not through swap(). I searched through Moralisā€™ documentation but couldnā€™t find anything that states how one could pass in smart contract constructor params with executeFunction().

const abi = await getSwapAbi();
const swapOpt = {
    contractAddress: swapConAdd,
    functionName: "swap",
    abi: abi,
    params: {
      buyerToken: stableAdd,
      buyer: buyerAdd,
      buyerAmt: Moralis.Units.Token(buyerAmt.toString(), stableDec.toString()),
      sellerAmt: Moralis.Units.Token(sellerAmt.toString(), "18")
    }
};

const swap = await Moralis.executeFunction(swapOpt);

How do I pass in params as constructor parameters with Moralis.executeFunction(option)?

usually you donā€™t call the constructor when you are making a transaction

you can also use directly web3 instead of Moralis.executeFunction of something doesnā€™t seem to work with executeFunction

Found out what was the problem, I was using the signTransaction method wrong, which was what made the approve fail in the backend.

Been searching high & low but not many examples out there, until I found one calling it this way.
Iā€™m supposed to pass the ABI to the approve transaction into the signTransaction method, then only run sendSignedTransaction.

Like so :

...

    const abi = await getTYSAbi(); // token ABI
    const tysContract = new web3.eth.Contract(abi, tysSmartCon); // token contract

    const buyerAmtStr = req.body.buyerAmt;
    const buyerAmt = await web3.utils.toWei(buyerAmtStr.toString(), 'ether');

    const tx = tysContract.methods.approve(swapSmartCon, buyerAmt);
    const estGas = await tx.estimateGas({ from: process.env.TYS_WALLET });
    const gasPrice = await web3.eth.getGasPrice();
    const data = tx.encodeABI();
    const nonce = await web3.eth.getTransactionCount(process.env.TYS_WALLET);

    const signedTx = await web3.eth.accounts.signTransaction(
      {
        to: tysContract.options.address, 
        data: data,
        gas: estGas,
        gasPrice: gasPrice,
        nonce: nonce, 
        chainId: process.env.BSC_CHAIN_ID
      },
      privateKey
    );

    const sendSignedTx = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);

...

I checked the allowance after & indeed, it has been updated to the value instead of the usual 0 & the swap was successful.

Glad I can finally put a close to this, thank you @cryptokid for your time.

1 Like

thank you @lime & @cryptokid we have been trying for 3 months set up a fiat gateway onramp for our ERC20 token, the moralis option is good but still trying to get api key after approval, then there is 1% charge, although your steps are spelled out, it is still above my head as being new to blockchain dev, is there someone we can pay to help us copy & paste these files? please pm if interested, thank you