Scripts
A script provides a light-weight method to query chain data.
It is executable Cadence code that can query for Flow execution state data but cannot modify it in any way.
Unlike a Flow transaction, a script is not signed and requires no transaction fees. Also unlike a transaction, a script can return a value back to the caller.
You can think of executing a script as a read-only operation, very similar to the eth_call
RPC method on Ethereum.
Scripts are currently executed on either the Access Nodes or the Execution Nodes based on the Access node configuration.
Scripts are defined by the following the Cadence code:
_10// The 'main' function is the entry point function and every script needs to have one._10access(all) fun main() {_10 // Cadence statements to be executed go here_10}
Scripts can return a typed value:
_10access(all) fun main(): Int {_10 return 1 + 2_10}
Scripts can also accept arguments:
_10access(all) fun main(arg: String): String {_10 return "Hello ".concat(arg)_10}
Scripts can call contract functions and query the state of a contract. To call a function on another contract, import it from its address and invoke the function:
_10import World from 0x01_10_10access(all) fun main(): String {_10 return World.hello()_10}
Scripts can also be run against previous blocks, allowing you to query historic data from the Flow network. This is particularly useful for retrieving historical states of contracts or tracking changes over time.
When to use a script?
Scripts can be used for the following:
- Validating a transaction before submitting it e.g. checking if the payer has sufficient balance, the receiver account is setup correctly to receive a token or NFT etc.
- Collecting chain data over time.
- Continuously verifying accounts through a background job e.g. a Discord bot that verifies users by their Flow account.
- Querying core contracts e.g. see staking scripts and events for querying staking and epoch related information, see the scripts directory under each of the core contract transactions for other core contracts related scripts.
Executing Scripts
Access API
A script can be executed by submitting it to the Access API provided by access nodes. Currently, there are three API endpoints that allow a user to execute scripts at the latest sealed block, a previous block height, or a previous block ID.
There are multiple SDKs implementing the above APIs for different languages:
Find a list of all SDKs here
Flow CLI
You can also execute a script by using the Flow CLI:
_10flow scripts execute ./helloWorld.cdc
A user can define their own scripts or can use already defined scripts by the contract authors that can be found by using the FLIX service.
Best Practices
Following are some recommendations on how to write efficient scripts:
-
Simpler and shorter scripts: Scripts, like transactions, are subject to computation limits (see limitations). It is recommended to run shorter and simpler scripts which have low time complexity for a faster response. If you have a script with several nested loops, long iteration, or that queries many onchain fields, consider simplifying the script logic.
-
Fewer state reads: A script reads execution state and to get a faster response, it is best to limit the amount of state that is read by the script.
-
Smaller length of array or dictionary type arguments: If your script requires an array or a dictionary as an argument where each element causes a state lookup, instead of making a single script call passing in a long list, make multiple calls with a smaller subset of the array or dictionary.
-
NFTCatalog: If your script uses the NFTCatalog functions, ensure that you use the latest functions and do not use any of the deprecated functions such as
getCatalog()
.
Limitations
-
Rate limit - Script execution is subjected to API rate-limits imposed by the Access nodes and the Execution nodes. The rate limits for the Public Access nodes hosted by QuickNode are outlined here.
-
Computation limit - Similar to a transaction, each script is also subjected to a computation limit. The specific value can be configured by individual Access and Execution node operators. Currently, the default compute (gas) limit for a script is 100,000.
-
Historic block data limit
- Script execution on execution nodes is restricted to approximately the last 100 blocks. Any request for script execution on an execution node on a past block (specified by block ID or block height) will fail if that block is more than 100 blocks in the past.
- Script execution on an access node can go much beyond the last 100 blocks but is restricted to the height when the last network upgrade (HCU or spork) occurred.