Building the tools for a chain nobody was building on
A chain had smart contracts and nothing to build them with. This is what a developer needs and the order the constraints let me build it in.
Motivation
A blockchain keeps a shared record that many machines agree on, and the agreement is what makes it useful. Every change is appended and nothing is ever removed, because a record that can be quietly edited is not a record anyone would trust.
That property is exactly wrong for the person writing the programs. Writing software means running a broken thing, watching it fail, fixing one line, and running it again from a clean start, forty times before lunch. You cannot do that on a machine whose entire purpose is never to forget. So somebody has to build you a private copy of the whole network that runs on your laptop and can be wiped, and until that copy exists, the platform is only usable by people patient enough to work without it.
In June 2020 I settled on smart contract documentation and tooling as the thing I would own at Terra. The chain was about to gain the ability to run programs at all, and nothing around that ability existed yet.
The extension seam
The reason I thought the work would pay off is a sentence I wrote on the tenth of June: this platform gives us the unique opportunity to play with the specifics of how a contract executes, because it gives us a simple basic model to extend.
The contract system on this chain compiles programs to WebAssembly, a compact instruction format originally built so code could run inside a browser, and hands each program a small, fixed set of functions for talking to the outside world. A short list of functions is a seam. Anything that narrow can be reimplemented, wrapped, logged, or replaced, and the program on the other side cannot tell the difference. That is what "a simple basic model to extend" means, and every tool in this post exists because that seam was there.
The documentation blocker
I spent most of June trying to write the smart contract documentation and not finishing it. On June 29 I found out why, and wrote it down with the answer attached:
Wrote the smart contract documentation with TerraCLI -- I realized that the thing holding me back from finishing writing was being blocked by not having a web interface for Terra smart contracts.. LESSON IN THERE: Figure out what's blocking you and resolve whether or not that will be there in time.
I had been waiting on a web interface that did not exist and had no date. What I noticed is that the documentation did not need it. Only the version of the documentation in my head needed it, the one with screenshots, where the reader clicks a button. Written against the command line instead, every instruction is a line the reader can copy, and the whole thing could be finished that week.
The general form is in that second sentence, and it has two halves people usually collapse into one. Finding what is blocking you is the easy half. Working out whether the blocker will actually arrive in time is the half that changes what you do, because a dependency with no date is not a dependency. It is a decision you have not made yet, sitting in the schedule disguised as something outside your control.
Working backwards from the end state
The day after, writing about a habit of starting things at the last minute, I put down the rule that has stayed with me longer than anything else here: start working backwards, focus on what you have to do, and then iterate on the prerequisites. The worked example in the entry is the same documentation, and the diagnosis is uncomfortable and correct. My process at the time was to research as much as I could about smart contracts, and what I should have done was start with the document and an outline.
Research and an outline behave differently under pressure. Research has no completion condition, so it expands to fill whatever time you give it and always feels productive, which is what makes it such a comfortable place to hide. An outline has a shape you can look at, and every gap in it is a specific question rather than a general inadequacy, so it converts an unbounded activity into a list.
The town hall outline
On July 1 I wrote a town hall outline, and it is the clearest single artifact from that year because it puts the diagnosis and the products on the same page.
The diagnosis was that the chain was boring, in the sense that you could only swap, send, and stake. The only way to build with it was to interact with the blockchain, and you could not build applications on top. That distinction is the whole problem in five words. Talking to a system across a wire and building inside it are different activities, and a platform that only supports the first has users but no ecosystem, because nothing anybody makes can live there.
The two things named to fix it:
- localterra, described as running a private Terra testnet with FCD, Station and Finder hooked up, which is the data service, the wallet and the block explorer, so you could easily test complicated contracts with resettable state. The one-line version later in the same outline is a one-click private testnet and ecosystem.
- create-terra-dapp, described as the quickest way to launch.
Resettable state is the load-bearing phrase. It is the append-only property from the top of this post, deliberately broken, in a copy of the network where breaking it is safe.
LocalTerra shipped two weeks later, on July 15, with documentation, and was wired up to work with Station. Later that month I wrote, in capitals, that I needed to write a nice smart contract with a good interface, because people will buy anything with a good interface. It is a blunt way to put it. It is also the through-line of everything above, since every decision in this post is a choice about what the developer touches.
Where the instrumentation belongs
The next design moved a layer down. In August 2021, in a list of tools I wanted for working with contracts, I wrote down a version of LocalTerra that listens to debug messages.
Where the listening happens decides what the tool can see. The obvious way to find out what a contract is doing is to make the contract say so, by adding logging to the program under test. That has two costs, and the second one is fatal. It changes the thing you are trying to observe, and it only works for programs you are allowed to edit, which rules out every contract somebody else deployed, which is most of the ones you actually want to understand. Putting the listener in the node instead inverts both. The local network reports what any contract did, whoever wrote it, because the contract is not the thing being asked. This is the same relocation I would make a year later when building a simulator, moving the instrumentation to the boundary the program runs against rather than into the program.
The project layout
The last piece from that period is a project scaffold, drafted in September 2021, which was meant
to replace a set of TypeScript scripts that automated contract tasks and were, in the draft's own
words, probably insufficient as a tool. The replacement was a Rust toolchain covering
architecture, development, testing, documentation and deployment, and the draft is specific in the
way that makes tooling real. It starts from one command, cargo houston new contract <dir-name>,
and the layout that command produces was written out in full:
- contracts/
- contract1/ # each contract is a crate
- src/
- lib.rs
- contract.rs # contract logic
- tests.rs # unit tests
- Cargo.toml
- integration-tests/
- test_XXX.rs
- docs/ # generated by `cargo houston docs build`
- scripts/ # invoke Houston functions programmatically
- deploy.rs
- migrate.rs
- Cargo.toml # workspace config and the Houston manifestThree decisions are visible in that tree. Unit tests sit next to the code they test while
integration tests get their own root, because they run against a deployed contract rather than a
function. The docs directory is generated rather than written, so it is an output and nobody edits
it by hand. And deployment lives in scripts/deploy.rs, which makes shipping a program in the
project rather than a paragraph in a README that drifts out of date the first time somebody
changes a flag.
One line in that draft is a question rather than a decision. Should users be able to enter information in a prompt? It sits there unanswered, which is what most of tool design actually looks like from the inside: not the architecture, which is usually the easy part, but a hundred small questions about what the person at the keyboard has to type, each of which has no theoretically correct answer and has to be picked anyway.