# Transacting With AxelarJS

Axelar is an on-chain solution for conducting interoperability between blockchains. As such, many teams building on top of Axelar opt to integrate Axelar-related functionality directly into their on-chain Solidity contracts. While this is a great option for many teams, it is not the only way to work with Axelar.

## **The AxelarJS library**

AxelarJS is a library for users who prefer to integrate with Axelar services from a JavaScript environment. This library contains the most commonly used interchain functionalities as well as other utilities that may not be available when writing in Solidity.

In this tutorial, you will utilize AxelarJS and execute the following as a single [Hardhat task](https://hardhat.org/hardhat-runner/docs/advanced/create-task):

* Send a GMP message with an aUSDC token from Celo to Fantom
    
* Estimate the gas costs of that transaction
    
* Query the status of your interchain transaction
    
* Recover and resend underfunded transactions
    

By the end, you should be familiar with the following AxelarJS features: [*gas-fee-estimation*](https://docs.axelar.dev/dev/general-message-passing/gas-services/pay-gas)*,*[*query transaction status*](https://axelar.network/blog/docs.axelar.dev/dev/axelarjs-sdk/tx-status-query-recovery#query-transaction-status-by-txhash)*,*[*recovering transactions*](https://docs.axelar.dev/dev/axelarjs-sdk/tx-status-query-recovery).

## **Getting Started**

Clone the [starter code](https://github.com/axelarnetwork/AxelarJS-Tutorial) from GitHub. The [`DistributionExecutable`](https://github.com/axelarnetwork/axelar-examples/blob/main/examples/evm/call-contract-with-token/DistributionExecutable.sol) contract should already be complete. This contract sends an aUSDC token along with a GMP message containing a list of addresses that the token will be sent to on the destination chain.

### Celo **and Fantom scripts**

There are also [two optional deployment scripts](https://github.com/benjamin852/AxelarJS-Tutorial/tree/starter/scripts) available for those who wish to deploy this contract themselves on the Celo and Fantom blockchains.

### **Set up the**`.env` file

To deploy the contract, set up your `.env` file to hold your wallet’s unique [mnemonic](https://wolovim.medium.com/ethereum-201-mnemonics-bb01a9108c38):

* Copy mnemonic from wallet
    
* New `.env` file in root folder
    
* In `.env` file: `MNEMONIC = "<mnemonic>”`
    
* Install [dotenv](https://www.npmjs.com/package/dotenv) lib
    
* Interact with your mnemonic via `process.env.MNEMONIC`
    

### **Deploy the contract**

Once you have set up the .env file, you should be able to run the deploy script. To deploy the `DistributionExample` contract to Celo, run

```javascript
npx hardhat run scripts/deployCelo.ts --network celo
```

After running the script you should see a log in your console saying the contract was deployed to Celo, along with the address of your contract.

### **Deploy to other chains**

The same deployment steps apply to any chain on the network. Just change the name of the `network` parameter. For example, to deploy the contract on Fantom, run

```javascript
npx hardhat run scripts/deployFantom.ts --network fantom
```

#### **Addresses for tutorial**

For simplicity’s sake, the rest of the tutorial will use the following two contracts:

* Celo: `0x953bE597934f1419E20cfFDa8D13B4EcF264057c`
    
* Fantom: `0xe41Abe529cf0491FAf854abc564314A511bF0CD2`
    

You can substitute these two addresses with any two addresses returned from deploying the `DistributionExample` contract on different chains. If you do not want to deploy your own instances of the contract and elect to use the pre-deployed addresses you must ensure to approve the address on the source chain to send the tokens on your behalf.

## **Access the deployed contract**

Now that you have deployed the `DistributionExample` contract, it is time to put it into use.

To access an instance of the contract navigate to the `hardhat.config.ts` file. Call the `getWallet()` helper function, which is defined in your utils folder. Pass in the source chain’s `RPC` along with an `ethers` object.

To access an instance of the deployed contract, pass in the contract’s address, the contract’s ABI, and the `connectedWallet` from the `getWallet()` function.

Your code should now look like this:

```javascript
task("sendToMany", "Send tokens across chain using axelarjs")
  .addParam("sourcechainaddr", "The source chain address")
  .addParam("destchainaddr", "The destination chain address")
  .setAction(async (taskArgs, hre) => {
    console.log("Let's do this!");
const connectedWallet = getWallet(chains[0].rpc, hre.ethers) 
const contract = new hre.ethers.Contract(
      taskArgs.sourcechainaddr,
      GMPDistribution.abi,
      connectedWallet
   )
});
```

## **Setup AxelarJS**

Now that the contract is deployed and linked with your wallet, you can begin the integration with AxelarJS.

In your `hardhat.config.ts` file, import the `AxelarQueryAPI` from the [@axelarjs package](https://www.npmjs.com/package/@axelar-network/axelarjs-sdk) already installed in the starter code:

```javascript
import {
 AxelarQueryAPI,
 Environment,
 EvmChain,
 AddGasOptions,
 GasToken,
 AxelarGMPRecoveryAPI,
 GasPaidStatus,
 GMPStatus,
} from '@axelar-network/axelarjs-sdk'
```

Import the following from the axelarjs-sdk, which you will use throughout the rest of the script.

Configure the API to point to the testnet environment right above your hardhat task:

```javascript
const sdkGmpRecovery = new AxelarGMPRecoveryAPI({ 
  environment: Environment.TESTNET,
})
```

## **Estimate gas costs**

An Axelar transaction has three different gas fees for transactions on the source chain, the Axelar chain, and the destination chain, respectively. The AxelarJS [estimateGasFee()](https://docs.axelar.dev/dev/axelarjs-sdk/axelar-query-api#estimategasfee) function estimates how much gas is needed to complete the interchain transaction across all three chains.

You can access the `estimateGasFee()` function from the `sdkQuery` variable you created earlier. This function takes the following parameters:

* Source chain name
    
* Destination chain name
    
* Source chain token
    
* The transaction’s [gas limit](https://shardeum.org/blog/what-is-a-gas-limit/)
    
* Gas multiplier — creates a buffer above the calculated gas fee to account for potential slippage throughout the transaction execution (for example, a 1.1 multiplier is a 10% buffer)
    
* Minimum gas price — estimated gas price will be overridden if it falls below this specified value
    

Call `estimateGasFee()` in your Hardhat task. It should look like this:

```javascript
const estimatedGasAmount = await sdkQuery.estimateGasFee(
  EvmChain.CELO,
  EvmChain.FANTOM,
  GasToken.CELO,
  700000,
  1.1,
  '500000'
)
```

## **Trigger the interchain transaction**

Now that you have calculated the estimated gas cost, you can begin the interchain translation. To do this, you will use the contract instance you created earlier. Simply call the `sendToMany()` function that is defined in the contract itself.

`sendToMany()` takes the following arguments:

* The name of the destination chain
    
* The address the transaction is being sent to on the destination chain
    
* The list of addresses that will be receiving the ERC-20 tokens once on the destination chain
    
* The symbol of the ERC-20 token being transferred
    
* The amount of ERC-20 tokens being transferred
    
* A gas value to pay for the interchain transaction — You can pass in an arbitrary amount of gas or pass in the estimated amount of gas that was calculated earlier. Any surplus gas that is sent along with the transaction will be refunded to the original sender.
    

The `sendToMany()` function call should look like this:

```javascript
const tx1 = await contract.sendToMany(
  EvmChain.FANTOM,
  taskArgs.destchainaddr,
  [
    '0x03555aA97c7Ece30Afe93DAb67224f3adA79A60f',
    '0xC165CbEc276C26c57F1b1Cbc499109AbeCbA4474',
    '0x23f5536D2C7a8ffE66C385F9f7e53a5C86F53bD1',
  ],
  GasToken.aUSDC,
  3000000,
  { value: estimatedGasAmount }
)

console.log('tx1.hash', tx1.hash)
```

Run this hardhat task:

```javascript
npx hardhat sendToMany --sourcechainaddr 0x68474f4c8124ec22940ca3a682c862c8447da6b6 --destchainaddr 0x69aBe660cB7b4C5Bfb7c47Ff6B02d5294DA7Ce19
```

You should be able to see the transaction executed from Celo to Fantom on the [Axelarscan block explorer](https://testnet.axelarscan.io/).

### **Obtain aUSDC**

The [Axelar faucet](https://discord.com/channels/770814806105128977/1002423218772136056) can provide you with all the `aUSDC` tokens you need.

## **Recover a transaction**

Great! Now you have transferred some `aUSDC`, along with a list of addresses, from Celo to Fantom. But what if you run into a situation where your transaction fails? Fortunately, AxelarJS has a handy transaction recovery component which can be very helpful in this situation. Let’s test it out to see how it works.

This time, you’ll call `sendToMany()` again, but you’ll pass in a deliberately low amount of gas to ensure that the transaction fails. Then you’ll use the AxelarJS transaction recovery service to fix the issue.

## **Execute a failing transaction**

Start by implementing a faulty `sendToMany()` function call:

```javascript
const tx2 = await contract.sendToMany(
  EvmChain.FANTOM,
  taskArgs.destchainaddr,
  [
    '0x03555aA97c7Ece30Afe93DAb67224f3adA79A60f',
    '0xC165CbEc276C26c57F1b1Cbc499109AbeCbA4474',
    '0x23f5536D2C7a8ffE66C385F9f7e53a5C86F53bD1',
  ],
  GasToken.aUSDC,
  3000000,
  { value: '1000' }
)
```

The entirety of this function call is identical, except for the value which is now set to a much smaller amount than before. This function call will quickly run out of gas. Run the task again and paste the resulting transaction hash into the [testnet block explorer](https://testnet.axelarscan.io/). It should show that the transaction has an insufficient base fee:

![Insufficient Base Fee Photo for AxelarJS Blog. Unable to fit full width of tx photo so going with this instead.](https://axelar.network/_next/image?url=https%3A%2F%2Fimages.ctfassets.net%2Fmjqoq33y05of%2F7aBr3EHtOguFbMkGoweeW4%2Fd1ea69846fc7488899d05856d45d2664%2FScreenshot_2024-01-18_at_4.45.33_PM.png&w=750&q=50 align="left")

## **Import the recovery API**

To get started set your gas options and the `AxelarGMPRecoveryAPI` to the testnet environment:

```javascript
const sdkGmpRecovery = new AxelarGMPRecoveryAPI({
  environment: Environment.TESTNET,
})
```

## **Query the transaction status**

Now you should have access to several useful functions in the recovery API. The first function you will be using is called [queryTransactionStatus()](https://docs.axelar.dev/dev/axelarjs-sdk/tx-status-query-recovery#query-transaction-status-by-txhash), which provides the status of the interchain transaction.

Pass in your faulty transaction hash as an argument:

```javascript
const tx2 = await contract.sendToMany(
  EvmChain.FANTOM,
  taskArgs.destchainaddr,
  [
    '0x03555aA97c7Ece30Afe93DAb67224f3adA79A60f',
    '0xC165CbEc276C26c57F1b1Cbc499109AbeCbA4474',
    '0x23f5536D2C7a8ffE66C385F9f7e53a5C86F53bD1',
  ],
  GasToken.aUSDC,
  3000000,
  { value: '1000' }
)
```

```javascript
let tx2Status
tx2Status = await sdkGmpRecovery.queryTransactionStatus(tx2.hash)
```

Once you have queried the transaction, the status will be `GAS_PAID_NOT_ENOUGH_GAS`. This is to be expected since you passed in only 1000 WEI to the transaction.

## **Add gas to a transaction**

This is where the recovery API comes to the rescue again. To fix this issue, you can call [addNativeGas()](https://docs.axelar.dev/dev/axelarjs-sdk/tx-status-query-recovery#21-native-gas-payment), which takes the following arguments:

* The source chain
    
* The hash of the transaction that needs the additional gas
    
* The `AddGasOptions` — several [optional parameters](https://github.com/axelarnetwork/axelarjs-sdk/blob/c763d1cc0f3f01e298887e74bfaced2a4e2bd92f/src/libs/types/index.ts#L160) you can include when increasing gas, including the amount, the refund address, and estimated gas used.
    
* an interface for adding gas to a transaction
    

`AddGasOptions` includes helpful configurations such as a refund address, gas multiplier, and destination chain. In this tutorial, you will only need to pass in the `evmWalletDetails`, which uses the private key of your wallet.

```javascript
const gasOptions: AddGasOptions = {
  evmWalletDetails: {
    privateKey: connectedWallet.privateKey,
  }
}
```

Once the `gasOptions` are configured, you can call the `addNativeGas()` function wrapped by the `tx2Status` object, which will only allow the `addNativeGas()` function to execute if the transaction status indicates that more funds are required:

```javascript

if (tx2Status.gasPaidInfo?.status == GasPaidStatus.GAS_PAID_NOT_ENOUGH_GAS) {
  console.log("inside if statement")
  const { success, transaction } = await sdkGmpRecovery.addNativeGas(
    EvmChain.CELO,
    tx2.hash,
    gasOptions
 )
  console.log('gas status:', tx2Status.gasPaidInfo?.status)
  console.log('adding gas transaction:', transaction?.blockHash)
  console.log(success, 'is success')
  break;
}
```

## **Execute a seamless transaction**

At this point, you have made use of all AxelarJS related functionality! The AxelarJS library may require a moment to locate the transaction hash and cannot immediately run functions for a hash that it considers unregistered.

To get all the logic of this Hardhat task to execute in a single call (for tutorial purposes only), you can wrap the `addNativeGas()` in a while loop. This loop will continually check the status of the `tx2` object until it is accounted for:

```javascript
while (tx2Status.status == GMPStatus.CANNOT_FETCH_STATUS || GasPaidStatus.GAS_UNPAID) {
  tx2Status = await sdkGmpRecovery.queryTransactionStatus(tx2.hash)
  if (tx2Status.gasPaidInfo?.status == GasPaidStatus.GAS_PAID_NOT_ENOUGH_GAS) {
    const { success, transaction } = await sdkGmpRecovery.addNativeGas(
      EvmChain.CELO,
      tx2.hash,
      gasOptions
    )

    console.log('gas status:', tx2Status.gasPaidInfo?.status)
    console.log('adding gas transaction:', transaction?.blockHash)
    console.log(success, 'is success')
    break;
  }
}
```

Now you should be able to call the entirety of the Hardhat task. Enter the following into your terminal:

```javascript
npx hardhat sendToMany --sourcechainaddr 0x261AD0f73B0062Fb5340e95861dF3EB9c1Fc6aD4 --destchainaddr 0x6bCA5a0B528333E824f0651d13e71A4343C1a5Bb
```

If you opted to deploy your own version of the `DistributionExecutable`, make sure to swap out those two addresses for your version of the deployed addresses.

## **Check your code**

The entirety of the `hardhat.config.ts` file should now look like this:

```javascript
import dotenv from 'dotenv'
import { task, HardhatUserConfig } from 'hardhat/config'
import '@nomicfoundation/hardhat-toolbox'
import chains from './chains.json'
import { getWallet } from './utils/getWallet'
import {
  AxelarQueryAPI,
  Environment,
  EvmChain,
  AddGasOptions,
  GasToken,
  AxelarGMPRecoveryAPI,
  GasPaidStatus,
  GMPStatus,
} from '@axelar-network/axelarjs-sdk'
import GMPDistribution from './artifacts/contracts/DistributionExecutable.sol/DistributionExecutable.json' dotenv.config()
const sdkQuery = new AxelarQueryAPI({ environment: Environment.TESTNET })
const sdkGmpRecovery = new AxelarGMPRecoveryAPI({
  environment: Environment.TESTNET,
})
task('sendToMany', 'Send tokens across chain using axelarjs')
 .addParam('sourcechainaddr', 'The source chain address')
 .addParam('destchainaddr', 'The destination chain address')
 .setAction(async (taskArgs, hre) => {
   console.log("Let's do this!")
   const connectedWallet = getWallet(chains[0].rpc, hre.ethers)
   const contract = new hre.ethers.Contract(
     taskArgs.sourcechainaddr,
     GMPDistribution.abi,
     connectedWallet
   )
  const estimatedGasAmount = await sdkQuery.estimateGasFee(
    EvmChain.CELO,
    EvmChain.FANTOM,
    GasToken.CELO,
    700000,
    1.1,
    '500000'
  )
  console.log(estimatedGasAmount, 'estimated')
   const tx1 = await contract.sendToMany(
     EvmChain.FANTOM,
     taskArgs.destchainaddr,
     [
       '0x03555aA97c7Ece30Afe93DAb67224f3adA79A60f',
       '0xC165CbEc276C26c57F1b1Cbc499109AbeCbA4474',
       '0x23f5536D2C7a8ffE66C385F9f7e53a5C86F53bD1',
     ],
     GasToken.aUSDC,
     3000000,
     { value: estimatedGasAmount }
   )
   console.log('tx1.hash', tx1.hash)
   const tx2 = await contract.sendToMany(
      EvmChain.FANTOM,
      taskArgs.destchainaddr,
      [
        '0x03555aA97c7Ece30Afe93DAb67224f3adA79A60f',
        '0xC165CbEc276C26c57F1b1Cbc499109AbeCbA4474',
        '0x23f5536D2C7a8ffE66C385F9f7e53a5C86F53bD1',
      ],
     GasToken.aUSDC,
     3000000,
      { value: '1000' }
    )
    let tx2Status
    tx2Status = await sdkGmpRecovery.queryTransactionStatus(tx2.hash)
    console.log('tx2.hash', tx2.hash)
    const gasOptions: AddGasOptions = {
      evmWalletDetails: {
        privateKey: connectedWallet.privateKey,
      }
    } while (tx2Status.status == GMPStatus.CANNOT_FETCH_STATUS || GasPaidStatus.GAS_UNPAID) {
      tx2Status = await sdkGmpRecovery.queryTransactionStatus(tx2.hash)
      if (tx2Status.gasPaidInfo?.status == GasPaidStatus.GAS_PAID_NOT_ENOUGH_GAS) {
        const { success, transaction } = await sdkGmpRecovery.addNativeGas(
          EvmChain.CELO,
          tx2.hash,
          gasOptions
        )
        console.log('gas status:', tx2Status.gasPaidInfo?.status)
        console.log('adding gas transaction:', transaction?.blockHash)
        console.log(success, 'is success')
        break;
      }
    }
  });
if (!process.env.MNEMONIC) throw 'mnemonic undefined'
const config: HardhatUserConfig = {
  solidity: '0.8.20',
  networks: {
    celo: {
      url: chains[0].rpc,
      accounts: { mnemonic: process.env.MNEMONIC },
      chainId: chains[0].chainId,
    },
    fantom: {
      url: chains[1].rpc,
      accounts: { mnemonic: process.env.MNEMONIC },
      chainId: chains[1].chainId,
    },
  },
}
export default config
```

## **Conclusion**

Congratulations! At this point, you should be able to see three transactions taking place:

* The successful `sendToMany()` sending the ERC20 token from Celo to Fantom
    
* The failing `sendToMany()` function
    
* The `addNativeGas()` function coming in to assist the failing `sendToMany()` call
    

Your terminal should show the following:

```bash
Let's do this!
tx1.hash 0xca61b0ab84314ba22a7932acf8531e7cabde7c4e8416a52b150effb23cf497ac
tx2.hash 0xae2d7f98df262db6332597c6e3188e50ba996bd826f219b0e1fd16bfe7fb0a4c
gas status: gas_paid_not_enough_gas
adding gas transaction: 0x51cbb678d99a01f744f6d78925a21d9cf40dc3829f72d90d7b0be0539605757d
true is success
```

You can view those transactions on either [Axelarscan](https://testnet.axelarscan.io/) or the source chain block explorer (in this case, [Celoscan](https://alfajores.celoscan.io/)). This tutorial just scratches the surface of the functionality that you have at your disposal with AxelarJS. We look forward to seeing what amazing projects you will build on top of Axelar!

## **Relevant documentation**

* [Send GMP messages with tokens](https://docs.axelar.dev/dev/general-message-passing/gmp-tokens-with-messages)
    
* [Axelar gas services](https://docs.axelar.dev/dev/general-message-passing/gas-services/intro)
    
* [GMP transaction status and recovery](https://docs.axelar.dev/dev/axelarjs-sdk/tx-status-query-recovery)
