Register a BNS Name
A Runbook can be used to easily register a new BNS name. Follow along with this guide to build a BNS Runbook and to practice some Runbook basics.
Project Setup
To set up the project, we will use the txtx new
command.
Run the command and fill out the following data:
> txtx new
Enter the name of this workspace: Txtx Guide
✔ Choose a Runbook type: · Maintenance: update settings, authorize new contracts, etc.
✔ Enter a name for the runbook (e.g., 'BNS Multisig') · Register BNS
✔ Enter the description for the runbook (optional) · Registers a BNS address on the Stacks blockchain
Created manifest txtx.yml
Created file runbooks/README.md
Created runbook runbooks/maintenance/register-bns.tx
Thie will generate the following manifest:
---
name: Txtx Guide
id: txtx-guide
runbooks:
- name: Register BNS
id: register-bns
description: Registers a BNS address on the Stacks blockchain
location: runbooks/maintenance/register-bns.tx
environments:
devnet:
stacks_network_id: devnet
stacks_api_url: http://localhost:3999
stacks_operator_address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC
testnet:
stacks_network_id: testnet
stacks_api_url: https://api.testnet.hiro.so
stacks_operator_address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC
mainnet:
stacks_network_id: mainnet
stacks_api_url: https://api.hiro.so
stacks_operator_address: SP2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1J5QKA2F
While this setup will work fine, we'll modify it slightly to keep our Runbook organized.
In the runbooks/maintenance/
folder, delete the register-bns.tx
file, and replace it with a folder called runbook-bns
containing three files:
runbooks/maintenance/register-bns/variables.tx
runbooks/maintenance/register-bns/main.tx
runbooks/maintenance/register-bns/outputs.tx
Finally, change the "location" field of the manifest to point to thisregister-bns
folder rather than just the file. When we execute the Runbook, all three files will be loaded. Having these separate files allows us to separate the logic of inputs, outputs, and the on-chain actions that will be run. Note, this is purely for organizational purposes and is completely optional.
Setting Variables
Next, we'll set some variables for our Runbook.
Here we can define some wallets that will be used for signing the transactions.
We can also set variables that can be configured at runtime to dictate the BNS name, namespace, and some other details needed for registering a BNS name.
Note: Because each variable
has the editable
field set to true
, each of these values will be editable in the Web UI.
To restrict an input from being editable in the Web UI, remove the editable
field, or set it to false
.
variables.tx
signer "operator" "stacks::web_wallet" {
expected_address = input.expected_address
}
variable "namespace" {
description = "TLD / Namespace"
value = "btc"
editable = true
}
variable "name" {
description = "Domain name to order"
value = "txtx"
editable = true
}
variable "salt" {
description = "Random salt to use for preventing front-running"
value = "00000000000000"
editable = true
}
variable "zonefile" {
description = "Zonefile to attach to the new domain"
value = "0000000000"
editable = true
}
Sending Transactions
Once our variables are set up, we can actually send some transactions.
We use the addon "stacks"
command to set the network_id
and rpc_api_url
for all Stacks plugin commands.
This will prevent these fields from having to be specified for every Stacks plugin command.
addon "stacks" {
network_id = input.stacks_network_id
rpc_api_url = input.stacks_api_url
}
To assemble our transactions, we will also need the current price of the BNS name we're registering.
We can get this using the stacks::call_readonly_fn
command to call a readonly smart contract function which contains the price.
The result of this transaction can be parsed and used in subsequent transactions.
action "get_name_price" "stacks::call_readonly_fn" {
description = "Preorder name"
contract_id = "ST000000000000000000002AMW42H.bns"
function_name = "get-name-price"
function_args = [
stacks::cv_buff(encode_hex(variable.namespace)),
stacks::cv_buff(encode_hex(variable.name))
]
sender = "ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC"
}
From there, we can assemble our transaction payloads according to the spec of the BNS contract.
We will need two transactions - one to preorder the BNS name and one to register it.
Both of these transactions will use the same action, stacks::call_contract
.
This command allows you to create a transaction payload, sign it, and broadcast it to the network.
action "send_name_preorder" "stacks::call_contract" {
description = "Send Preorder ${variable.name}.${variable.namespace} transaction"
contract_id = "ST000000000000000000002AMW42H.bns"
function_name = "name-preorder"
function_args = [
stacks::cv_buff(
ripemd160(sha256(
[
encode_hex("${variable.name}.${variable.namespace}"),
encode_hex(variable.salt)
]
))
),
stacks::cv_uint(action.get_name_price.value),
]
signer = signer.operator
confirmations = 1
}
action "send_name_register" "stacks::call_contract" {
description = "Register name"
contract_id = "ST000000000000000000002AMW42H.bns"
function_name = "name-register"
function_args = [
stacks::cv_buff(encode_hex(variable.namespace)),
stacks::cv_buff(encode_hex(variable.name)),
stacks::cv_buff(encode_hex(variable.salt)),
stacks::cv_buff(encode_hex(variable.zonefile)),
]
signer = signer.operator
depends_on = [action.send_name_preorder.tx_id]
}
For each of the transactions, we set the signer
to reference the signer we created in the inputs
file (wallet.operator
).
Aside from the transaction payload, the only difference between the name preorder and registration transactions is the depends_on
field.
This field can be specified to create dependencies in the order of transaction execution.
Because the BNS registration transaction has a depends_on
field referencing the BNS preorder transaction, it won't be executed until the preorder transaction is confirmed.
We can put this all together to have our main.tx
file:
main.tx
action "defaults" "stacks::set_default_network" {
network_id = input.stacks_network_id
rpc_api_url = input.stacks_api_url
}
action "get_name_price" "stacks::call_readonly_fn" {
description = "Preorder name"
contract_id = "ST000000000000000000002AMW42H.bns"
function_name = "get-name-price"
function_args = [
stacks::cv_buff(encode_hex(variable.namespace)),
stacks::cv_buff(encode_hex(variable.name))
]
sender = "ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC"
}
action "send_name_preorder" "stacks::send_contract_call" {
description = "Send Preorder ${variable.name}.${variable.namespace} transaction"
contract_id = "ST000000000000000000002AMW42H.bns"
function_name = "name-preorder"
function_args = [
stacks::cv_buff(
ripemd160(sha256(
[
encode_hex("${variable.name}.${variable.namespace}"),
encode_hex(variable.salt)
]
))
),
stacks::cv_uint(action.get_name_price.value),
]
signer = wallet.operator
confirmations = 1
}
action "send_name_register" "stacks::send_contract_call" {
description = "Register name"
contract_id = "ST000000000000000000002AMW42H.bns"
function_name = "name-register"
function_args = [
stacks::cv_buff(encode_hex(variable.namespace)),
stacks::cv_buff(encode_hex(variable.name)),
stacks::cv_buff(encode_hex(variable.salt)),
stacks::cv_buff(encode_hex(variable.zonefile)),
]
signer = wallet.operator
confirmations = 1
depends_on = [action.send_name_preorder.tx_id]
}
Displaying Outputs
Finally, we will specify some output
commands to display data at the end of the runbook execution.
We put this in the outputs.tx
file purely for organizational purposes.
outputs.tx
output "bns_name" {
value = "Registering ${variable.name}.${variable.namespace}"
}
output "name_price" {
value = action.get_name_price.value
}
output "name_preorder_tx_link" {
value = "https://explorer.hiro.so/txid/${action.send_name_preorder.tx_id}?chain=${input.stacks_network_id}"
}
output "name_preorder_result" {
value = action.send_name_preorder.result
}
output "name_register_tx_link" {
value = "https://explorer.hiro.so/txid/${action.send_name_register.tx_id}?chain=${input.stacks_network_id}"
}
output "name_register_result" {
value = action.send_name_register.result
}
Executing the Runbook
With txtx installed, run the following command in your terminal:
txtx run register-bns
This will run txtx on http://localhost:8488
. Follow the prompts in the Web UI to execute the runbook.