Language and Syntax Documentation
This is the documentation for the txtx language. Txtx files that you write describe which blockchains and networks to use, what data to retrieve, and what transactions to broadcast. The txtx language also lets you define dependencies between resources and create multiple similar constructs from a single block.
Syntax
Txtx files contain a series of code blocks that look something like this:
variable "query_path" {
description = "An input that can be edited in the web UI!"
value = "details"
editable = true
}
action "http_query" "std::send_http_request" {
description = "This action will make a GET request to the specified URL!"
url = "https://example.com/${variable.my_var.query_path}"
}
output "status_code" {
description = "This output will be displayed in the Outputs section of the web UI!"
value = action.http_query.status_code
}
Each block has a command type (in this example variable
, action
, and output
), a reference name ("query_path"
, "http_query"
, and "status_code"
), and the inner data of the block (everything between the { ... }
).
Some command types also require that a command is specified ("std::send_http_request"
).
The inner data of the block is dictated by the command type and command that are specified.
We'll get into the different command types soon.
First, let's observe a few things.
Command References
One command can reference the outputs of another command to build a chain of dependent commands. When you execute a Runbook, txtx creates a graph of all the commands to ensure they are always executed in the correct order. This does mean that dependency cycles need to be avoided when creating a Runbook.
Another command's output can be referenced in a block using command_type.ref_name.output_name
. Here are some examples:
...
variable "my_var" {
description = variable.another_var.value // references the `value` output of a `variable` named `another_var`
value = action.my_action.data // references the `data` output of an `action` named `my_action`
}
Functions
The txtx standard library provides some functions that can be written in-line, as opposed to writing full command blocks with named arguments. These functions can be extended via addons.
These functions can look like explicit function calls (e.g. add_uint(1, 3)
), or they can look like in-line arithmetic operations (e.g. 1 + 3
).
Functions can reference other command outputs, or they can be stored in new command outputs.
Here's an example using a few functions:
variable "one" {
value = 1
}
variable "two" {
value = 2
}
variable "addem_up" {
value = variable.one + variable.two
}
output "add_some_more" {
value = add_uint(variable.addem_up + variable.one, variable.two)
}
...
Now that that's covered, let's dive into the different command types and data sources for a Runbook.
Manifest & CLI Inputs
Inputs can be provided to the Runbook by passing them in as a CLI input, or by specifying them in a txtx.yml
manifest file.
If the same input is provided both in the CLI and in a manifest, the CLI input will take precedence.
To see information on how to use the CLI, check out the CLI documentation.
In the manifest, these inputs can be grouped by an environment key, making it easy to use the same Runbook across multiple environments.
Here is an example config with environment variables:
---
name: BNS Runbook
environments:
development:
stacks_network_id: testnet
stacks_api_url: https://api.testnet.hiro.so
staging:
stacks_network_id: testnet
stacks_api_url: https://api.testnet.hiro.so
production:
stacks_network_id: mainnet
stacks_api_url: https://api.mainnet.hiro.so
runbooks:
- name: Register BNS
description: This runbook pre-orders and registers a user-input BNS name.
location: "./run"
In any of the .tx
files loaded by this runbook, the inputs input.stacks_network_id
and input.stacks_api_url
will be available in the global scope.
When the Web UI loads this runbook, the first action item will allow you to select which environment to load.
Selecting a new environment will reload the current runbook with the new environment variables being injected into the execution.
State Management
Txtx can manage state across Runbook executions. When running contract deployments, the state management can be used to detect any changes to the contract code and Runbook inputs to determine if the Runbook needs to be re-executed. Txtx will prevent a re-execution of a Runbook if there aren't any changes to the contract code or Runbook inputs.
To enable state management, provide the state
value and a location
to store the state file in the txtx.yml
:
...
runbooks:
- name: Deployment
location: "./run"
state:
location: states
Variables
Variables can be used to store values that can be used by other constructs and that can be edited by users in the Web UI.
If the variables's editable
field is unspecified or set to false
, the variable will appear on the Web UI in the Variables Review
section as a readonly value that can be verified.
If the editable
field is set to true
, however, the variable will appear as an editable field in the Web UI.
The optional description
field can be provided to add aditional context to the variable.
Finally, the type
field can be provided to restrict the type of values that are valid for the variable.
In most cases, the type
can be inferred from the initial value
field.
Here is an example variable:
variable "my_var" {
description = "Enter your birthday"
value = "MM/DD/YYYY"
type = "String"
editable = true
}
Addons & Defaults
Addon blocks allow you to specify what addons will be used by the Runbook.
Any fields declared inside the addon block can be referenced by any actions that are part of that addon.
This will allow you to omit fields when using custom actions from that addon.
The following example declares the stacks
addon and sets the network_id
and rpc_api_url
fields as defaults:
addon "stacks" {
network_id = env.stacks_network_id
rpc_api_url = env.stacks_api_url
}
With this default added to a .tx
file, any actions from the Stacks addon can omit both the network_id
and rpc_api_url
fields.
Flows
Flows allow you to execute a Runbook multiple times with different inputs for each execution.
Any fields specified in the flow block can be referenced by any actions that are part of that flow via flow.field_name
.
This can be used in conjunction with the addon
block in helpful ways:
// declare some flows
flow "mainnet" {
chain_id = 1
rpc_api_url "https://mainnet.infura.io/v3/"
}
flow "arbitrum" {
chain_id = 42161
rpc_api_url "https://arbitrum.infura.io/v3/"
}
// declare the evm addon with
addon "evm" {
chain_id = flow.chain_id
rpc_api_url = flow.rpc_api_url
}
// the rest of the runbook can now use the evm addon without specifying chain_id or rpc_api_url,
// and will be executed once for each flow
...
Signers
Addons can define signers that provide various ways to sign transactions when using txtx. These signers can be used to sign transactions via a mnemonic or secret key, to prompt users to connect their web wallet and sign in the Txtx Web UI, to sign transactions asyncronously via secure enclave, to define multisig wallets, and more. Each addon signer implementation will have its own use cases and documentation.
Here is an example of a signer in use:
signer "alice" "stacks::web_wallet" {
expected_address = input.expected_address
}
action "my_tx" "stacks::sign_transaction" {
... tx data
signer = signer.alice
}
This example defines a signer named alice
, which uses the stacks::web_wallet
signer.
This signer definition will generate a prompt in the Web UI to connect a wallet,
provide a public key via message signature, and to sign all transactions using this wallet as a signer.
Actions
Actions are multi-purpose constructs that are defined by addons and by the txtx standard library. Each action defines its own set of inputs (some optional and some required) that can be supplied to the action, what happens during each call to the action, and what outputs are created by the action, which can be referenced by subsequent commands.
Some examples of types of actions include making http requests and outputting the result, encoding transaction data to match a given chain's codec, signing a transaction with a wallet and outputting the signed transaction bytes, or broadcasting a transaction to a network.
Here is an example action:
action "my_ref" "stacks::call_contract" {
description = "Encodes the contract call, prompts the user to sign, and broadcasts the set-token function."
contract_id = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.pyth-oracle-v1"
function_name = "verify-and-update-price-feeds"
function_args = [
encode_buffer(variable.bitcoin_price_feed),
encode_tuple({
"pyth-storage-contract": encode_principal("${input.pyth_deployer}.pyth-store-v1"),
"pyth-decoder-contract": encode_principal("${input.pyth_deployer}.pyth-pnau-decoder-v1"),
"wormhole-core-contract": encode_principal("${input.pyth_deployer}.wormhole-core-v1")
})
]
}
output "tx_id" {
value = action.my_ref.tx_id
}
output "result" {
value = action.my_ref.result
}
Modules
Coming soon.
Outputs
The output command can be used to display data at the end of the runbook execution. Here is an example of the output command:
output "my_output" {
description = "An example output. I hope it equals 8."
value = 4 + 4
}