Estimate gas
Because transaction fees vary depending on the resources required to execute specific operations, it can use useful to calculate potential fees by testing contract functions using the Pact REPL and .repl files.
This coding project demonstrates the basic steps for calculating the gas required to execute any method in a .repl file.
This coding project assumes you're familiar with the basic built-in functions for testing smart contracts in your local development environment using .repl files.
As an extension of the Local testing, the project demonstrates how to gas-related built-in functions with the loans-estimate.pact module as the sample smart contract to test.
In this coding project, you'll learn about:
- Using the built-in environment configuration functions.
- Setting and updating gas limits.
- Testing the gas consumed for individual functions.
- Combining functions in transaction blocks.
Before you begin
Before starting this project, verify your environment meets the following basic requirements:
- You have a GitHub account and can run
gitcommands. - You have installed the Pact programming language and command-line interpreter.
- You have installed the
kadena-clipackage and have a working directory with initial configuration settings. - You have a local development node that you can connect to that runs the
chainweb-nodeprogram, either in a Docker container or on a physical or virtual computer. - You must have at least one account that's funded with KDA on at least one chain for deployment on the local development network or the Kadena test network.
- You should be familiar with the basics for defining modules and using keysets.
Get the starter code
To get started:
-
Open a terminal shell on your computer.
-
Clone the
pact-coding-projectsrepository by running the following command:git clone https://github.com/kadena-docs/pact-coding-projects.git -
Change to the
07-estimate-gasdirectory by running the following command:cd pact-coding-projects/07-estimate-gasIf you list the contents of this directory, you'll see the following files:
starter-estimate-gas.replprovides a starting point testing theloansmodule.loans-estimate.pactprovides the final code for theloansmodule with the functions that you'll estimate gas consumption for.loans-estimate.replprovides the final version of the test cases for estimating the gas consumed by functions defined in theloansmodule.
-
Open and review the
starter-estimate-gas.replfile.This file outlines the tasks you need to complete for the Estimate gas project. Follow the embedded instructions to work through the coding challenges on your own or use the detailed instructions and code provided in the next sections.
-
(Optional) Copy the
starter-estimate-gas.replfile or create a newestimate-gas.replfile in your code editor to use for this coding project.
Set environment data
As you learned in Local testing, most smart contracts require some information to be defined in the testing environment so that it's available for module functions to use.
To prepare environment data for testing:
-
Open the
estimate-gas.replfile in your code editor. -
Add the
env-sigsbuilt-in function to enable theloans-admin-keysetto sign transactions and have unrestricted capabilities.(env-sigs [{"key": "loan-admin-keyset", "caps": []}]) -
Add the
env-databuilt-in function to define theloans-admin-keysetkey and predicate function.(env-data {"loans-admin":
{ "keys": ["loan-admin-keyset"], "pred": "keys-all" } }) -
Add a transaction to define the
freenamespace for the module.(begin-tx "Define namespace")
(define-namespace "free" (read-keyset "loans-admin" ) (read-keyset "loans-admin" ))
(commit-tx) -
Add a transaction to initialize gas modeling by setting the
env-gasmodelbuilt-in function totableand theenv-gaslimitbuilt-in function to150000or a similar value.(begin-tx "Initialize gas modeling for testing")
(env-gasmodel "table")
(env-gaslimit 150000)
(commit-tx)
Load the module and call functions
After you initialize gas modeling, you are ready to begin calling the env-gas built-in function to report the gas consumed by individual function calls or by transactions that call multiple functions.
This coding project demonstrates both how to calculate the gas consumption for specific function calls and how to combine multiple function calls into a single transaction and estimate the gas required to complete the transaction.
To load the module and call functions:
-
Begin a transaction.
(begin-tx "Call the inventory-key function to estimate gas") -
Load the
loans-estimate.pactfile into the REPL.(load "loans-estimate.pact") -
Call the
inventory-keyfunction with theloanId:stringandowner:stringparameters.(inventory-key "loanId-1" "Las Pistolas") -
Retrieve the gas consumed using the
env-gasbuilt-in function.(env-gas) -
Commit the transaction.
(commit-tx) -
Reset the gas retrieved to zero before calling the next function.
(env-gas 0)At this point, you have a loans-estimate.repl file that looks similar to the following to estimate gas consumption of one function:
;; ===================================================================
;; 1 Set environment data
;; ===================================================================
(env-sigs [{"key": "loan-admin-keyset", "caps": []}])
(env-data { "loans-admin":
{ "keys": ["loan-admin-keyset"], "pred": "keys-all" } })
(begin-tx "Define namespace")
(define-namespace "free" (read-keyset "loans-admin" ) (read-keyset "loans-admin" ))
(commit-tx)
(begin-tx "Initialize gas modeling for testing")
(env-gasmodel "table")
(env-gaslimit 150000)
(commit-tx)
;; ===================================================================
;; 2 Load the loans-estimate.pact file and call a function
;; ===================================================================
(begin-tx "Call inventory-key function to estimate gas")
(load "loans-estimate.pact")
(inventory-key "loanId-1" "Las Pistolas") ;; loanId, owner
(env-gas) ;; Retrieve the gas consumed.
(commit-tx)
(env-gas 0) ;; Reset the gas consumed to zero. -
Test that the gas consumed is returned by running the following command:
pact --trace loans-estimate.replYou should see output similar to the following:
loans-estimate.repl:0:0-0:53: "Setting transaction signatures/caps"
loans-estimate.repl:1:0-2:58: "Setting transaction data"
loans-estimate.repl:4:0-4:29: "Begin Tx 0 Define namespace"
loans-estimate.repl:5:2-5:85: "Namespace defined: free"
loans-estimate.repl:6:0-6:11: "Commit Tx 0 Define namespace"
loans-estimate.repl:7:0-7:48: "Begin Tx 1 Initialize gas modeling for testing"
loans-estimate.repl:8:2-8:24: "Set gas model to table-based cost model"
loans-estimate.repl:9:2-9:23: "Set gas limit to 150000"
loans-estimate.repl:10:0-10:11: "Commit Tx 1 Initialize gas modeling for testing"
loans-estimate.repl:12:0-12:56: "Begin Tx 2 Call inventory-key function to estimate gas"
loans-estimate.repl:13:2-13:30: "Loading loans-estimate.pact..."
loans-estimate.pact:4:0-4:18: "Namespace set to free"
loans-estimate.pact:5:3-5:65: "Keyset write success"
loans-estimate.pact:7:3-175:1: Loaded module free.loans, hash 1dh4D_KYqbDxW6L-WECJC71HUyq2wpmC7wy_ztsUxiQ
loans-estimate.pact:181:0-181:20: "TableCreated"
loans-estimate.pact:182:0-182:35: "TableCreated"
loans-estimate.pact:183:0-183:33: "TableCreated"
loans-estimate.repl:13:2-13:30: ()
loans-estimate.repl:14:2-14:43: "loanId-1:Las Pistolas"
loans-estimate.repl:15:2-15:11: 6031
loans-estimate.repl:16:0-16:11: "Commit Tx 2 Call inventory-key function to estimate gas"
loans-estimate.repl:17:0-17:11: "Set gas to 0"
Load successfulIn this example, the gas consumed by the
inventory-keyfunction is 6031.
Call each function separately
You can add similar transactions to retrieve the gas consumed for each function defined in a module.
In the following example, each transaction imports the free.loans module and calculates the gas consumed for a specific function:
;; ===================================================================
;; 3 Call each function to retrieve the gas consumed
;; ===================================================================
(begin-tx "Call create-a-loan to estimate gas")
(use free.loans)
(create-a-loan "loanId-1" "Ponderosa" "Valley Credit" 16000) ;; loanId, loanName, entity, amount
(env-gas)
(commit-tx)
(env-gas 0)
(begin-tx "Call assign-a-loan to estimate gas")
(use free.loans)
(assign-a-loan "txid-1" "loanId-1" "Studio Funding" 10000) ;; txid, loanId, buyer, amount
(env-gas)
(commit-tx)
(env-gas 0)
(begin-tx "Call sell-a-loan to estimate gas")
(use free.loans)
(sell-a-loan "txid-2" "loanId-1" "buyer2" "Studio Funding" 2000) ;; txid, loanId, seller, buyer, amount
(env-gas)
(commit-tx)
(env-gas 0)
(begin-tx "Call read-a-loan to estimate gas")
(use free.loans)
(read-a-loan "loanId-1") ;; loanId
(env-gas)
(commit-tx)
(env-gas 0)
(begin-tx "Call read-all-loans to estimate gas")
(use free.loans)
(read-all-loans)
(env-gas)
(commit-tx)
(env-gas 0)
As before, you can test that the gas consumed is returned for each function by running the following command:
pact --trace loans-estimate.repl
In this example, the functions from the loans module returned the following results:
...
..."Begin Tx 3 Call create-a-loan to estimate gas"
loans-estimate.repl:26:2-26:11: 88
...Begin Tx 4 Call assign-a-loan to estimate gas"
loans-estimate.repl:33:2-33:11: 168
..."Begin Tx 5 Call sell-a-loan to estimate gas"
loans-estimate.repl:40:2-40:11: 132
..."Begin Tx 6 Call read-a-loan to estimate gas"
loans-estimate.repl:47:2-47:11: 19
..."Begin Tx 7 Call read-all-loans to estimate gas"
loans-estimate.repl:54:2-54:11: 40004
...
Combine multiple functions
In some cases, you might want to estimate the gas required to execute a set of functions instead of individual functions.
You can do so by combining the functions into a single transaction.
The following example demonstrates combining several function into a single transaction then retrieving the gas consumed.
Note that you might need to increase the value you set for the env-gaslimit built-in function when you are returning the gas for multiple functions in a single transaction.
The following example demonstrates combining multiple functions into a single transaction, increasing the gas limit value, and returning the gas consumed for all of the functions executed:
;; ===================================================================
;; 4 Combine multiple functions in a transaction and retrieve gas
;; ===================================================================
(begin-tx "Call multiple functions in a transaction")
(use free.loans)
(create-a-loan "loanId-2" "Renovation" "RiverBank" 140000)
(read-a-loan "loanId-2")
(read-all-loans)
(read-loan-inventory)
(read-loans-with-status INITIATED)
(env-gaslimit 280000)
(read-loans-with-status ASSIGNED)
(env-gas)
(commit-tx)
Review
This project introduced several of the built-in REPL-only functions for calculating gas. The project included examples of the following use-cases:
- Calculating the gas required to execute individual functions.
- Calculating the gas required to execute a transaction that combined multiple functions.
Through these examples, you learned how to:
- Initiate table-based gas modeling.
- Set an initial gas limit.
- Report the gas consumed.
- Reset the gas to zero.
You also learned that you can update the gas limit, if needed.