Setting up Trigger Actions With Chainhooks
With chainhooks, our goal is to help developers approach blockchain development using event based architectures. If you are familiar with If This Then That (IFTTT) applications, then you already have a basic understanding of how chainhooks work. With chainhooks, developers can trigger actions, based on predicates they can write. Predicates can, non-exhaustively, be:
- A certain amount of SIP-10 tokens were transferred.
- A particular address received some tokens on the Stacks blockchain.
- A particular print event was emitted by a contract.
- A particular contract was involved in a transaction.
- A quantity of BTC was received on a Bitcoin address.
- A POX transfer occurred on the Bitcoin chain.
You read that right: chainhooks can target both Stacks and Bitcoin. Chainhooks are possible thanks to a new Event Observer layer designed for Bitcoin and Stacks that considers the Bitcoin chain as a first class citizen.
Chainhooks make the process of reacting to blockchain events easier and more reliable with a dedicated open source library for dealing with chains that can fork. Chainhoooks will be of great help for speeding up the development of projects like:
- Cross-chain protocols: As illustrated in the cBTC example below, chainhooks can be used for coordinating cross-chain actions.
- Oracles: An event emitted on-chain triggers a centralized logic that can be committed on-chain once computed.
- Chain Indexers: Protocols like Arkadiko, ALEX or Gamma are focused on a particular slice (or view) of the blockchain. With chainhooks, they can easily extract the information that they need for building (or rebuilding) databases they need for their frontend.
- Distributed nodes: The chainhook event observer was designed as a library written in Rust, which makes it very portable. Bindings can easily be created from other languages (Node, Ruby, Python, etc), making this tool a very convenient and performant library, usable by anyone.
To serve those use-cases, chainhooks were written and designed with a few constraints in mind:
- Portability and performance: The chainhook event observer was designed as a library: it is currently embedded in Clarinet and can be run on your local machine, or it can be executed server side and propagate HTTP events to your other components. Using Rust makes it easily usable from other languages by leveraging some language bindings (supported by languages like Node, Ruby, Python, Swift, etc). It also gives developers an edge in terms of performance (low level, multithreading) as well as throughput. At Hiro, we will start leveraging both in the coming months.
- Correctness: Observing the chain using an event observer is more challenging than it sounds, mostly because of the fact that blockchains can fork. A given transaction can be included in a microblock, but that transaction can end up discarded by the next miner. It’s even possible for a transaction to be included in an anchor block that does not end up on the canonical fork. There are many ways to end up with a state slightly differing from the canonical state that could cause snowball effects, resulting in users issuing transactions that can end up failing or worse, just because the off-chain view is not reflecting the data on-chain.
To get a sense of how chainhooks work in action, check out this sample app of cBTC to see how chainhooks can be used to wrap/unwrap BTC on the Stacks chain.
In terms of deployment lifecycle, you can start using chainhooks locally today, using the latest versions of Clarinet. We are also in the process of testing a server deployment of the chainhook event observer—please open an issue on GitHub if this is an option you’d like to explore.
If you’d like to be responsible for your own deployment, this component is open sourced and can be deployed in your own infrastructure. No lock-in!
We are impatient to see more protocols built on top of this new component, so don’t hesitate to contact us if you’d like to get more information, or need help getting started. To get in touch, open an issue on GitHub.
Interact with Contracts on Mainnet Through Requirements
Requirements is our first attempt in helping developers working with other contracts that are already deployed on Mainnet. This feature recently got some traction and interest from a few developers, so we’ve been using this momentum to iterate on our initial design and ended up shipping it!
By declaring a deployed contract as a requirement (either using the command “clarinet requirements add <contract-id>” or manually editing Clarinet.toml), developers enable their contract to interact with these specified external contracts. A few typical use cases include:
- Implementing a trait (like SIP-009/SIP-010) by using a trait template already deployed on mainnet.
- Testing token trading locally by using swap contracts already deployed on mainnet.
We’d love to get feedback for this particular feature. Feel free to open an issue on GitHub if you end up being limited in some way while using it.
With Clarinet, we do our best to help developers be productive and cautious.
Productivity is achieved thanks to simulated chains, which are the underlying infrastructure powering the commands clarinet test, clarinet console and clarinet check: an in-memory virtual machine is spun-up, and interprets Clarity code. We call this mode “Simnet”—and in this mode developers trade high fidelity for productivity.
Cautiousness is achieved by letting developers run their contract on an actual local blockchain. Configuring and starting a Stacks blockchain node can be pretty tedious: because Stacks blocks are anchored in the Bitcoin chain, developer’s would need to start a Bitcoin node, a Stacks node, and then orchestrate both a Bitcoin miner and a Stacks miner.
Then if the developer wants to explore these 2 blockchains, they’d need to start a Stacks blockchain API, as well as a Bitcoin Explorer and a Stacks Explorer. Developers would need to publish their contracts to this local blockchain, and only after all of these steps, could developers finally test their contracts manually, by issuing transactions. If everything is done manually, each iteration could easily take 20 minutes, when done correctly.
Clarinet comes with a command clarinet integrate, which does all this heavy lifting for you. All the coordination explained above is performed on the developer's behalf through this command. With the latest improvements, it now takes 1 to 2 minutes to get an environment up and running. We call this mode “Devnet,” and in this mode developers trade productivity for high fidelity.
clarinet integrate is not a new feature, and we are continuously updating it. Recently, this feature gained some traction, and with traction comes feedback. With feedback, improvements and fixes. Those improvements included several updates targeting stabilization and new features that could be enabled. In particular, we will soon upgrade Clarinet’s terminal UI and make more data accessible.
In the latest version, developers actually have the ability to spin up their own hyperchain node and get an early feel for this incoming component.
Customize Your Smart Contract Deployment
We’ve seen a lot of teams building on Stacks turn to Stacks.js for smart contract deployments because Stacks.js allows developers to customize their deployment plan. This approach works well and brings a lot of flexibility, which can be required in certain deployments. However, in many cases, deployments are straightforward, boring, error prone, and are just an additional point of failure that we could easily standardize and remove from developer’s plates.
So that’s what we did.
To the question, “can you trust deployment plans?” we now have an answer. With the latest version of Clarinet, every action in Clarinet now relies on deployment plans.
- Starting a clarinet console: This action computes a deployment plan in memory, based on the contracts and requirements listed in your Clarinet.toml manifest, and applies its actions on an emulate chain internally called Simnet.
- Executing a test suite: This action triggers the same actions as described in the previous bullet.
- Executing clarinet integrate: This action computes a deployment plan written to disk, and once the network is ready, it will execute on your behalf to automatically deploy your contracts.
- Even the VS Code extension relies on simnet deployment plans to ensure that all the protocols are correctly being assembled.
Deployment plans were designed to exclude sensitive information, meaning they can be committed and shared, which is also very convenient in the context of audits. Now you can share your deployment plans with security engineers to help them understand how your contracts will be deployed without revealing information like secret keys.
With the first version of deployment plans, developers have a number of actions available to them today, including:
- emulated-contract-publish: Deploy a contract in an in-memory simulated chain (simnet only).
- emulated-contract-call: Call a contract that has been deployed in an in-memory simulated chain (simnet only).
- requirement-publish: Deploy an external contract on another testnet / devnet network using another wallet + search, and replace all the references to this contract in the local contracts to deploy (devnet / testnet only).
- contract-publish: Deploy a contract (devnet / testnet / mainnet).
- contract-call: Call a contract (devnet / testnet / mainnet).
- send-bitcoin: Simple bitcoin transfer from a p2pkh address to a p2pkh address (experimental, regtest / testnet / mainnet).
The first version of this release is already very useful, but it will become even more handy in the future—the next step for this feature is simplifying contract versioning and upgrades. We may also explore more Bitcoin actions (involving Bitcoin script) for deployment plans in the near future: we essentially want to help developers get started and use certain Bitcoin programming patterns. Feel free to reach out if you are interested in certain actions.
There you have it. Four new features. To see a list of all of Clarinet’s features, check out the README in the Clarinet repo. If you have questions or things you would like to see improved, chat with us on the #clarinet channel on Discord or open an issue on GitHub.