0

Been stuck on swapping tokens using ethers js, here's the code I'm using:

const https_provider = new ethers.providers.JsonRpcProvider(https_provider_addres)
const wallet = new ethers.Wallet.fromMnemonic(mnemonic)
const account = wallet.connect(provider)
const https_account = wallet.connect(https_provider)

const erc20_ABI =
    [
        {
            "constant": true,
            "inputs": [],
            "name": "name",
            "outputs": [
                {
                    "name": "",
                    "type": "string"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        },
        {
            "constant": false,
            "inputs": [
                {
                    "name": "_spender",
                    "type": "address"
                },
                {
                    "name": "_value",
                    "type": "uint256"
                }
            ],
            "name": "approve",
            "outputs": [
                {
                    "name": "",
                    "type": "bool"
                }
            ],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
        },
        {
            "constant": true,
            "inputs": [],
            "name": "totalSupply",
            "outputs": [
                {
                    "name": "",
                    "type": "uint256"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        },
        {
            "constant": false,
            "inputs": [
                {
                    "name": "_from",
                    "type": "address"
                },
                {
                    "name": "_to",
                    "type": "address"
                },
                {
                    "name": "_value",
                    "type": "uint256"
                }
            ],
            "name": "transferFrom",
            "outputs": [
                {
                    "name": "",
                    "type": "bool"
                }
            ],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
        },
        {
            "constant": true,
            "inputs": [],
            "name": "decimals",
            "outputs": [
                {
                    "name": "",
                    "type": "uint8"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        },
        {
            "constant": true,
            "inputs": [
                {
                    "name": "_owner",
                    "type": "address"
                }
            ],
            "name": "balanceOf",
            "outputs": [
                {
                    "name": "balance",
                    "type": "uint256"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        },
        {
            "constant": true,
            "inputs": [],
            "name": "symbol",
            "outputs": [
                {
                    "name": "",
                    "type": "string"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        },
        {
            "constant": false,
            "inputs": [
                {
                    "name": "_to",
                    "type": "address"
                },
                {
                    "name": "_value",
                    "type": "uint256"
                }
            ],
            "name": "transfer",
            "outputs": [
                {
                    "name": "",
                    "type": "bool"
                }
            ],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
        },
        {
            "constant": true,
            "inputs": [
                {
                    "name": "_owner",
                    "type": "address"
                },
                {
                    "name": "_spender",
                    "type": "address"
                }
            ],
            "name": "allowance",
            "outputs": [
                {
                    "name": "",
                    "type": "uint256"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        },
        {
            "payable": true,
            "stateMutability": "payable",
            "type": "fallback"
        },
        {
            "anonymous": false,
            "inputs": [
                {
                    "indexed": true,
                    "name": "owner",
                    "type": "address"
                },
                {
                    "indexed": true,
                    "name": "spender",
                    "type": "address"
                },
                {
                    "indexed": false,
                    "name": "value",
                    "type": "uint256"
                }
            ],
            "name": "Approval",
            "type": "event"
        },
        {
            "anonymous": false,
            "inputs": [
                {
                    "indexed": true,
                    "name": "from",
                    "type": "address"
                },
                {
                    "indexed": true,
                    "name": "to",
                    "type": "address"
                },
                {
                    "indexed": false,
                    "name": "value",
                    "type": "uint256"
                }
            ],
            "name": "Transfer",
            "type": "event"
        }
    ]

const addresses = {
    WAVAX: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
    router: "0x60aE616a2155Ee3d9A68541Ba4544862310933d4",
    factory: "0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10",
}

const router = new ethers.Contract(
    addresses.router,
    [
        'function getAmountsOut(uint amountIn, address[] memory path) public view returns (uint[] memory amounts)',
        'function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)',
        'function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)',
    ],
    account
)

const USDC_address = '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E'

const STG_address = "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590"
const STG_contract = new ethers.Contract(
    STG_address,
    erc20_ABI,
    https_account
)

const swap = async () => {

    console.log('swap starting')

    const amountIn = "22146823544065952776"

    const amounts = await router.getAmountsOut(amountIn, [
        STG_address,
        USDC_address
    ])

    const amountOutMin = amounts[1].sub(amounts[1].div(12))
    console.log('amountOutMin: ' + amountOutMin)

    const tx = await router.swapExactTokensForTokens(
        amountIn,
        amountOutMin,
        [STG_address, USDC_address],
        https_account.address,
        Math.floor(Date.now() / 1000) + 60 * 6, // 6 mins from current
        {
            gasLimit: 5000009999
        }
    )

    console.log('Swap done!')
    const receipt = await tx.wait()
    console.log('Transaction receipt' + receipt)
}

const init = async () => {
    console.log('before contract approve')

    //
    let receipt = STG_contract.connect(https_account).approve("0x60aE616a2155Ee3d9A68541Ba4544862310933d4", ethers.utils.parseUnits('1000.0', 18)).then((results) => {
        console.log(results)
        swap()
    })

    console.log(receipt)
    console.log('await contract approve happened')
}

init()

The approve works fine, I get the expected console output, tx hash (can verify on snowtrace etc)

Problem is with the actual swap, I get the amounts out, but after when attempting swapExactTokensForTokens basically nothing happens - it just keeps hanging forever, no errors, nothing

Any ideas?

2 Answers 2

0

I would not go with a huge gasLimit number on avalanche, as in my experience it causes an extra gas fee. First, try to change it to at least 1_000_000. Ideally, estimate it via await router.estimateGas['swapExactTokensForTokens'](...params).

I also agree with 0xSanson's reply - you have to await for tx to be confirmed. In your case, you're using a callback to await tx to be signed, and not confirmed.

1
  • Thanks, I managed to get a few swaps happen by retrying and also tweaking around the gas settings - since I want this automated I need to find a reliable method that wouldn't rely on me retrying etc so that estimateGas could be the answer So sometimes it works, sometimes I'm getting gas / nonce errors basically at the moment
    – soundcake
    Sep 23 at 15:06
0

First I would do try using a single provider. Now you have provider and https_provider, which I guess are different? It's possible that provider isn't working so I would change the following:

//const account = wallet.connect(provider)
const account = wallet.connect(https_provider)

Also I would try the following, waiting for the approve receipt before swapping:

const init = async () => {
    console.log('before contract approve')

    // let's complete the approve first
    let tx_receipt = await STG_contract.connect(https_account).approve("0x60aE616a2155Ee3d9A68541Ba4544862310933d4", ethers.utils.parseUnits('1000.0', 18));
    let receipt = await tx_receipt.wait();
    console.log(receipt)

    // onto the swap
    await swap()
}

If the above two solutions don't work, I guess it's a nonce problem. It's possible that by sending two txs in a short time the provider used the same nonce in both, so the second one was dropped. To fix this you need to get the nonce of the account, and specifically use nonce in approve and nonce+1 in swap.

const nonce = await account.getTransactionCount();

let tx_receipt = await STG_contract.connect(account).approve(
    "0x60aE616a2155Ee3d9A68541Ba4544862310933d4",
    ethers.utils.parseUnits('1000.0', 18),
    {
        nonce: nonce,
    }
);

// ...

const tx = await router.swapExactTokensForTokens(
    amountIn,
    amountOutMin,
    [STG_address, USDC_address],
    https_account.address,
    Math.floor(Date.now() / 1000) + 60 * 6, // 6 mins from current
    {
        nonce: nonce+1,
        gasLimit: 5000009999
    }
)
3
  • Thanks, yeah the provider was unnecessary, just the https one is in use I managed to move a few swaps through, but also got a few 'nonce too low' errors - how to best set custom nonce? Can you get a total tx count via ethers js and then +1 etc?
    – soundcake
    Sep 23 at 15:03
  • @soundcake Yeah, with account.getTransactionCount(). I added some lines to the answer.
    – 0xSanson
    Sep 23 at 15:42
  • Thanks, Nice one:) This does help, I do need to figure out a way of reliably performing a bunch of transactions consecutively (it'll be automated eventually). Currently still running into gas / nonce issues, but this is helpful indeed
    – soundcake
    Sep 23 at 16:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.