This is the documentation for the txtx language. Runbook 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
Runbooks 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.
Command References
One command can reference the outputs of another command to build a chain of dependent commands. When you execute a Runbook, surfpool 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 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)}
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: protocol-deploymentrunbooks:- name: Deploy Protocoldescription: This runbook deploys the protocol.location: ./deploymentenvironments:development:network_id: localnetrpc_api_url: http://localhost:8899devnet:network_id: devnetrpc_api_url: https://api.devnet.solana.commainnet:network_id: mainnetrpc_api_url: https://api.mainnet-beta.solana.com
In any of the .tx files loaded by this runbook, the inputs input.network_id
and input.rpc_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
Surfpool 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. Surfpool 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: Deploy Protocollocation: ./deploymentstate: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 additional context to the variable. Here is an example variable:
variable "my_var" {description = "Enter your birthday"value = "MM/DD/YYYY"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 svm addon and sets the
network_id and rpc_api_url fields as defaults:
addon "svm" {network_id = input.network_idrpc_api_url = input.rpc_api_url}
With this default added to a .tx file, any actions from the SVM 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 flowsflow "solana" {rpc_api_url "https://api.mainnet-beta.solana.com"}flow "eclipse" {rpc_api_url "https://mainnetbeta-rpc.eclipse.xyz"}// declare the evm addon withaddon "svm" {network_id = "mainnet"rpc_api_url = flow.rpc_api_url}// the rest of the runbook can now use the svm 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 surfpool. 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 Surfpool Web UI, to sign transactions asynchronously 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" "svm::web_wallet" {expected_address = input.expected_address}action "my_tx" "svm::process_instructions" {... instruction datasigners = [signer.alice]}
This example defines a signer named alice, which uses the svm::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 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 "deploy_hello_world" "svm::deploy_program" {description = "Deploy the hello_world program"program = svm::get_program_from_anchor_project("hello_world")authority = signer.authoritypayer = signer.payer}output "signature" {value = action.deploy_hello_world.signature}
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}
Is this page helpful?