Home

Sending adjustable fee transactions with DijetsJS

Overview#

The objective of this document is to provide and explain sending transactions with dynamic fees using JavaScript. Make sure you have followed the tutorial on adjusting the dynamic fees using MetaMask. There, we have explained the key concepts related to dynamic fees and EIP1559 type of transactions.

Prerequisites#

Installing Dependencies#

Open the terminal and install the following dependencies in a new folder.

  • Ethers
  • dijets
  • dotenv

_10
npm install ethers dijets dotenv

Setting up Environment and Project#

To send a transaction we need to sign it using our private key. But private key should not be hard coded in the code, rather must be fetched through some environment variables. Make a .env file in the root folder with the following content.


_10
PRIVATEKEY=<TYPE_YOUR_PRIVATE_KEY_HERE>

Now make a new file named app.js in the root folder, which will be our main and only file with the sendDjtx() function. Follow the rest of the tutorial by understanding and pasting the provided snippets sequentially in the app.js file.

Importing Dependencies and Private Key#


_10
const ethers = require("ethers")
_10
const Dijets = require("dijets").Dijets
_10
require("dotenv").config()
_10
_10
const privateKey = process.env.PRIVATEKEY

Setting up HTTP Provider Connected with Dijets TestNet Network#

Using the HTTP provider, we will connect to one of the nodes on the Dijets TestNet network. Using this provider we will send the signed transaction to the network. You can also connect to Mainnet using the URL - https://dijets.ukwest.cloudapp.azure.com:443/ext/bc/C/rpc


_10
// For sending a signed transaction to the network
_10
const nodeURL = "https://dijets.ukwest.cloudapp.azure.com:443/ext/bc/C/rpc"
_10
const HTTPSProvider = new ethers.providers.JsonRpcProvider(nodeURL)

Setting up Utility Chain APIs for Estimating Base and Priority Fees#

To estimate the max fee and max priority fee on the network, we will be using Utility Chain APIs. We can use the Utility Chain through a DijetsJS instance connected to the network as shown below.


_10
// For estimating max fee and priority fee using CChain APIs
_10
const chainId = 98200
_10
const dijets = new Dijets(
_10
"dijets.ukwest.cloudapp.azure.com:443",
_10
undefined,
_10
"https",
_10
chainId
_10
)
_10
const uchain = dijets.UChain()

Setting up Wallet#

A wallet is required for signing transactions with your private key and thus making it valid.


_10
// For signing an unsigned transaction
_10
const wallet = new ethers.Wallet(privateKey)
_10
const address = wallet.address

Function for Estimating Max Fee and Max Priority Fee#

The function calcFeeData() estimates the max fee and max priority fee per gas according to network activity using the Utility Chain APIs. This function returns max fee and max priority fee per gas in units of nDJT or gwei (1 DJT = 10^9 gwei).


_22
// Function to estimate max fee and max priority fee
_22
const calcFeeData = async (
_22
maxFeePerGas = undefined,
_22
maxPriorityFeePerGas = undefined
_22
) => {
_22
const baseFee = parseInt(await cchain.getBaseFee(), 16) / 1e9
_22
maxPriorityFeePerGas =
_22
maxPriorityFeePerGas == undefined
_22
? parseInt(await cchain.getMaxPriorityFeePerGas(), 16) / 1e9
_22
: maxPriorityFeePerGas
_22
maxFeePerGas =
_22
maxFeePerGas == undefined ? baseFee + maxPriorityFeePerGas : maxFeePerGas
_22
_22
if (maxFeePerGas < maxPriorityFeePerGas) {
_22
throw "Error: Max fee per gas cannot be less than the max priority fee per gas"
_22
}
_22
_22
return {
_22
maxFeePerGas: maxFeePerGas.toString(),
_22
maxPriorityFeePerGas: maxPriorityFeePerGas.toString(),
_22
}
_22
}

Function to Create, Sign and Send Transaction#

The function sendDjtx() takes 4 arguments -

  • amount - Amount of DJT to send in the transaction
  • address - Destination address to which we want to send DJT
  • maxFeePerGas - Desired maximum fee per gas you want to pay in nDJT
  • maxPriorityFeePerGas - Desired maximum priority fee per gas you want to pay in nDJT
  • nonce - Used as a differentiator for more than 1 transaction with same signer

The last 3 arguments are optional, and if undefined is passed, then it will use the calcFeeData() function to estimate them. Each transaction with the same data and parameters is differentiated by a nonce value. If there are more than 1 transactions with the same nonce signed by the same address, then only 1 of them with the highest effective priority fee will be accepted.

Note that the nonce parameter should only be used when you are either re-issuing or cancelling a stuck transaction.


_46
// Function to send DJT
_46
const sendDjtx = async (
_46
amount,
_46
to,
_46
maxFeePerGas = undefined,
_46
maxPriorityFeePerGas = undefined,
_46
nonce = undefined
_46
) => {
_46
if (nonce == undefined) {
_46
nonce = await HTTPSProvider.getTransactionCount(address)
_46
}
_46
_46
// If the max fee or max priority fee is not provided, then it will automatically calculate using Utility Chain APIs
_46
;({ maxFeePerGas, maxPriorityFeePerGas } = await calcFeeData(
_46
maxFeePerGas,
_46
maxPriorityFeePerGas
_46
))
_46
_46
maxFeePerGas = ethers.utils.parseUnits(maxFeePerGas, "gwei")
_46
maxPriorityFeePerGas = ethers.utils.parseUnits(maxPriorityFeePerGas, "gwei")
_46
_46
// Type 2 transaction is for EIP1559
_46
const tx = {
_46
type: 2,
_46
nonce,
_46
to,
_46
maxPriorityFeePerGas,
_46
maxFeePerGas,
_46
value: ethers.utils.parseEther(amount),
_46
chainId,
_46
}
_46
_46
tx.gasLimit = await HTTPSProvider.estimateGas(tx)
_46
_46
const signedTx = await wallet.signTransaction(tx)
_46
const txHash = ethers.utils.keccak256(signedTx)
_46
_46
console.log("Sending signed transaction")
_46
_46
// Sending a signed transaction and waiting for its inclusion
_46
await (await HTTPSProvider.sendTransaction(signedTx)).wait()
_46
_46
console.log(
_46
`View transaction with nonce ${nonce}: https://utility-explorer.swedencentral.cloudapp.azure.com/tx/${txHash}`
_46
)
_46
}

This function calculates transaction hash from the signed transaction and logs on the console, the URL for transaction status on Dijets Utility Chain explorer.

Calling the sendDJT() Function#

There are various ways to call this function. We may or may not pass the optional arguments like max fee and max priority fee. It is recommended to set the max fee as the maximum price per gas that you are willing to pay for a transaction, no matter how high or low the base fee will be, as at max you will only be charged the provided max fee, along with a small priority fee above the base fee.

If you do not pass these arguments, then it will automatically estimate the max fee and priority fee from the network. For example, let's say, Bob want to pay 100 nDJT per gas for a transaction and a small tip of 2 nDJT, then Bob will call the following function.


_10
// setting max fee as 100 and priority fee as 2
_10
sendDjtx("0.01", "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC", 100, 2)

This function should not be used without a max fee per gas. As you will have to pay the estimated price, even if it is higher than your budget.

Different Scenarios#

Max FeePriority FeeComment
undefined2Calculates the max fee by adding the provided priority fee with the estimated base fee. Note that the max fee will now be capped by baseFee + priorityFee, which can consume all the provided priority fees.
100undefinedEstimates the priority fee and uses the provided max fee. If the estimated priority fee is more than the provided max fee, then it throws an error.
undefinedundefinedIt will estimate the base fee and priority fee from the network, and will add both the values to calculate the max fee per gas. Again, you have to pay whatever will be estimated.

Re-issuing a Stuck Transaction#

Sometimes during high network activity, some transactions can get stuck as pending for a long time, due to relatively lower effective tip than the other transactions in the pool. We can either re-issue the same transaction with a higher priority fee or cancel the transaction altogether. To re-issue the stuck transaction, you can send a new transactiob with same amount and data but with higher priority fee and same nonce value as the stuck transaction. The transaction with lower effective tip will automatically be rejected (due to same nonce).

You can also cancel the stuck transaction, by sending a new transaction but keeping the amount to 0, with a higher priority fee and same nonce. Let's say, the above transaction with a nonce value of 25 has stuck. You can then re-issue a new transaction with same nonce, but higher priority fee this time.


_10
// reissuing transaction with nonce 25
_10
sendDjtx("0.01", "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC", 100, 10, 25)
_10
_10
// cancelling transaction with nonce 25
_10
sendDjtx("0", "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC", 100, 10, 25)

Conclusion#

You have learned about creating, signing, and sending transactions with dynamic fee parameters to the Utility Chain of Dijets network using JavaScript. It also explained, how to re-issue or cancel a stuck transaction, by sending a transaction with the same nonce. This tutorial points out the recommended way for choosing max fee cap and max priority fee cap for transactions and can also work as a general guide for all the EVM-based chains.