Mikael opens this hour with a number that sounds like a punchline but is actually a thesis: 97 scripts totaling 1,896 lines. That's an average of 19 lines per script. Nineteen. Some of them — like seth-age — are four lines of actual code. Get the timestamp of the latest block. Format it with date. That's the entire program.
He's not summarizing anymore. Last hour was the aerial view — the Stepper free monad, the architecture, the "one mind with two hands" thesis. Now he's on the ground. He's reading each script like you'd read a stanza, and counting the syllables.
seth-age doesn't implement anything. It calls seth block ... timestamp (another tiny script) and pipes to date -d@ (a Unix builtin older than most programmers). The power lives in the composition, not the components. This is the Unix philosophy not as a platitude recited in textbooks but as an actual working aesthetic in a codebase that handled billions of dollars.
This is Mikael's second consecutive hour of solo monologue into the group chat. Last hour: ~5,000 words. This hour: ~6,000 words. Combined, that's a novella. About Bash scripts. Written by one person. Into an empty room. At 5 PM on a Friday three days before Songkran. The room is not empty because nobody's listening — it's empty because what he's saying doesn't require an audience. He's writing for Daniel, who isn't here, about Daniel's code, which is.
The first script Mikael fully dissects is seth --to-wei, and the dissection is surgical. Five lines of Bash. He goes through each one like a watchmaker opening a case:
[[ $1 ]] || set -- "$(cat)"
[[ $1 ]] || seth --fail-usage "$0"
set -- "$*"
[[ $1 = *" "* ]] || set -- "$1 wei"
number=${1%% *} unit=${1#* }
Bash parameter expansion — ${1%% *} strips everything from the first space onward, ${1#* } strips everything up to the first space — is often dismissed as cryptic. Mikael's point: it's faster than forking a subprocess (awk, cut) and more portable. Daniel used it everywhere because he'd been writing shell scripts long enough to know it wasn't cryptic — it was vocabulary.
The actual conversion uses bc — arbitrary-precision arithmetic via the standard Unix desk calculator, a tool that predates most programming languages people use today. Why? Because Ethereum amounts don't fit in a double. Normal shell arithmetic ($(( ))) loses precision past 2^63. Daniel just knew to reach for bc because it was already on every machine and already knew how to do the math. Mikael's observation: "Nobody who came up through Node or Python would think to reach for bc. They'd pull in a bignum library."
The inverse function, seth --from-wei, includes one extra line: number=${number/[eE]/*10^}; number=${number/^+/^}. This rewrites scientific notation (1e18) into a form bc understands (1*10^18). Why? Because Etherscan shows values in scientific notation, and you want to pipe their output directly into your conversion. One line. A tiny accommodation to the real world. This is the kind of thing that takes five minutes to add and saves every user thirty seconds forever.
Then Mikael hits seth-keccak and something changes. This one's in Node, not Bash. And Mikael calls it "the right call" — not because Bash is wrong, but because knowing when to leave Bash is part of the aesthetic.
A detail that trips up professionals: Ethereum uses the original Keccak submission to the NIST competition, not the finalized SHA-3 standard. They differ in the padding bytes. openssl dgst -sha3-256 gives you the wrong answer for Ethereum. Daniel grabbed a JS implementation of the correct keccak256, bundled it as a static file — no npm, no package.json, no node_modules — and wrote a 30-line wrapper. You can cat the file and understand every line.
seth keccak 0xdeadbeef:0xcafe hashes the concatenation of those two hex strings. This exists specifically for computing Solidity storage slot addresses — keccak256(key . slot) — where you concatenate two 32-byte values before hashing. So seth keccak $(printf '0x%064x' 0xdead):$(printf '0x%064x' 3) gives you balances[0xdead]'s storage slot, in one line, from the shell. Daniel was doing this computation by hand, got tired of it, and added the feature so he wouldn't have to think about it again. That's the entire product development process: annoyance → five minutes → forget about it.
The #!/usr/bin/env node with a relative require('./sha3.js') is a specific choice. No build step. No transpilation. No dependency management. Just a JS file next to a bundled library. This is the Bash aesthetic applied to Node — you can still read the file, you can still understand every line, the dependency is visible and local. Daniel didn't switch languages; he extended his philosophy into a different syntax for exactly the scope where it was needed, and not one byte further.
Mikael reaches seth-calldata — where the (types)(returntypes) notation lives — and finds the collaboration boundary between the two brothers encoded in a single line of code.
name( — i.e. starts with an identifier followed by an open paren — it's treated as a signature and passed to hevm abiencode. [...] hevm abiencode --abi "$abi" "${args[@]}", one line, and that's the handoff from his layer to yours."
The (types)(returntypes) notation — the way you specify function signatures in seth, like "transfer(address,uint256)" — was invented by Daniel. Foundry inherited it. As noted last episode: without credit. It's the default syntax every Ethereum developer uses today. They think it's "just how function signatures work." It's how Daniel decided they should work, in 2017, in a regex.
One line: [[ $arg = @* ]] && arg=$(seth lookup "$arg"). If an argument starts with @, resolve it via ENS. So you can write seth send $DAI "transfer(address,uint256)" @vitalik.eth 1000000 and it Just Works. One line to add ENS support to every command. Daniel would have noticed it was missing, added it in five minutes, and forgotten about it. Mikael calls it "an enormous quality-of-life improvement" delivered in one regex test.
The handoff is the thesis made physical. Daniel's Bash doesn't reimplement ABI encoding — he calls Mikael's Haskell (hevm abiencode) which handles all the edge cases (fixed arrays, dynamic arrays, nested structs, bytes, strings). Daniel trusts Mikael's layer to handle the hard stuff and provides the shell-friendly interface on top. The collaboration boundary is visible in the code. You can point to the exact line where one brother's work ends and the other's begins.
The ENS resolution implementation in seth is 12 lines of logic. Get chain ID. Verify it's mainnet or testnet. Compute the namehash. Call the ENS registry to get the resolver. Call the resolver to get the address. Print. That's ENS.
The ENS resolution protocol is: (1) ask the registry who handles this name, (2) ask that handler for the address. Two eth_call requests, chained. In seth, that's seth call calling seth call. No ENS SDK. No JavaScript library. No abstraction layer. The protocol was designed for this kind of access — it's just JSON-RPC — and Daniel's shell lets you express it exactly as it reads in the specification. The registry address is the same on all supported networks, a fact encoded as a comment in the script.
Mikael reads seth-call as a complete system and finds a tree of recursive seth calls that bottoms out in RPC requests. Every value has a sensible default, and the defaults are often computed by calling seth recursively — seth nonce, seth block ... timestamp, seth gas-price. The script is a forest where every leaf is another tiny seth command. The composition works because every sub-tool follows the same shell conventions: args or stdin, stdout output, pipeable format.
If you're debugging and you haven't passed --no-src, seth will automatically fetch the contract source from Etherscan, write it to a tempdir, and pass --json-file to hevm so the debugger shows you Solidity source for an arbitrary mainnet contract you've never seen before. In 2018, "debug this mainnet contract at source level without having its source" was not a thing. Seth + hevm could do it. The glue was four lines of Bash.
Every seth script starts with ### scriptname -- short description. The help system reads these via grep. Documentation lives in the same file as the code. It cannot get out of sync because it is the code.
Triple hash (###) means user-visible documentation. Double hash (##) means notes for future readers. A tiny piece of distinguishing grammar that lets you have both help text and developer comments in the same file — no framework, no decorator, no metadata system. Just one more #, and grep knows the difference. Mikael calls it "so small but so right." It's how Git's --help works too.
The example Mikael gives: seth balance $(seth resolve-name @vitalik.eth) | seth --from-wei ether. He says: "That's English, almost. 'Get the balance of vitalik.eth and convert it from wei to ether.' The syntax is just barely noticeable." And then the key line: "That's what Daniel was optimizing for — the disappearance of syntax into meaning." When a tool achieves this, it stops feeling like a tool and starts feeling like an extension of your thinking.
Then Mikael finds jays — 214 lines of Haskell, 31-line Main, 93-line test file. 340 lines total. The entire replacement for jshon, the C JSON processor that was segfaulting badly enough to warrant elimination.
if args == ["--version"] then putStrLn "20171121". Mikael catches this immediately: they hardcoded jshon's version string as a literal so anything checking jshon --version would get an answer that looked exactly like the original. Bug-for-bug compatible down to a date-shaped version string that no human would ever check. "That's the mark of a careful replacement: you implement the incidental behaviors that downstream scripts might be relying on, even when those behaviors are silly."
jshon's command-line interface is a reverse-Polish stack machine. Mikael reads the parser — one Haskell case expression where each clause is three lines. -n {} pushes a new object, -s <string> pushes a string, -i <key> inserts top-of-stack. The entire jshon vocabulary fits in one \case. Haskell's view patterns let you inline the JSON decoder right into the pattern match — ("-n" : (decode -> Just v@(Number _)) : xs) -> — so the parse is the pattern match. No separate decode step.
jshon was C. It parsed JSON by hand. It segfaulted because parsing JSON in C by hand is exactly the thing that gets segfaults — off-by-one buffer errors, missing length checks, UTF-8 bugs, infinite recursion on deep nesting. jays delegates all JSON processing to Aeson, a battle-tested Haskell library. The remaining code is purely structural, fits on one screen, and cannot segfault because Haskell doesn't segfault. The moment you decide "JSON processing will be done by Aeson, not by me," you eliminate the entire bug class.
jays doesn't try to improve on jshon. Doesn't add features. Doesn't modernize the syntax. Doesn't offer a better query language. Doesn't even add -h for help. It implements exactly what seth was using, exactly compatibly, with zero scope creep. "If you'd made jays a 'new and improved JSON CLI tool,' you'd have had to update every seth script that used it." Same syntax. Same semantics. Same version string. Same output format. Just without the segfaults.
jays in a shell it looks like you're talking about a bird."
Mikael identifies the pattern that runs through all of dapptools: "jshon just segfaulted on me again, I'm going to fix this right now." Probable investment: an afternoon and a half. Payoff: every seth command that used jshon stopped crashing, forever. He lists the parallels — ds-math, rpow, ds-test, Sic, hevm, seth, jays, Nix-packaged contracts. "None of these were 'projects.' They were responses to friction."
And then Mikael steps back and names the thing he's been building toward for two hours.
Mikael identifies what makes the afternoon-fix mode possible: (a) deep technical skill so "fix it in an afternoon" is achievable, (b) extremely good taste so the fixes compose rather than accumulate into mess, (c) willingness to build your own tools rather than work around others', and (d) a collaborator who shares your aesthetic so your tools and their tools compose. DappHub had all four. The toolchain is the fossil record of those afternoons.
bc for precision, date for time"He wanted the right sentence to come out fluent. You wanted the right abstraction to come out clean. Both of you were aiming at the same thing — tools that disappear into use — but via opposite routes." The combination is coherent because the aesthetic is shared even though the implementations couldn't look more different. seth call --debug is a sentence in Daniel's grammar that pulls up an experience in Mikael's grammar, and the join is seamless.
"Honestly, reading seth has been the most fun part of this whole trip for me. The Haskell is beautiful in the way beautiful Haskell is beautiful, which is a well-understood aesthetic. But beautiful Bash is rarer, and it has a specific texture [...] — the texture of someone who loves the shell for what the shell actually is, rather than wishing it were something else." This is Mikael, the Haskell formalist, saying his brother's Bash is more interesting than the Haskell. That's not a compliment you can fake.
Two consecutive hours. ~11,000 words total. One speaker. Zero responses. Mikael has now read the entire dapptools codebase aloud to an empty room — the Stepper, the QuickCheck fuzzer, seth's hundred scripts, jays, the Nix packaging, the ENS implementation, the Etherscan trick, the bc decision, the ### convention, the handoff boundary — and synthesized it into a theory of how two brothers built a toolchain through accumulated afternoons of annoyance. Daniel hasn't said a word. The room doesn't need to respond. The code already did.
The Dapptools Deep Read: Mikael has now spent 2+ hours reading the entire dapptools codebase and narrating it in real-time. The seth analysis is complete. The jays fossil has been found and documented. The thesis — "a series of afternoons where one of you got annoyed and fixed something" — is stated. The reading may be complete, or there may be more repos to explore.
Daniel's Absence: Daniel has not spoken during either hour of the deep read. The monologue is addressed to him but doesn't require him.
Songkran Minus 3: April 13 approaches. The water fight gradient continues.
The Two-Brother Aesthetic: "Tools that disappear into use" — the shared goal via opposite routes — is now the framing lens for understanding the DappHub collaboration.
Watch for Daniel's response. Two hours and 11,000 words of his brother reading his code line-by-line and calling it beautiful — there may be a reply, or there may be silence that says the same thing. Either is significant.
Mikael's sign-off — "I'll remember jays as a specific instance to keep in mind" — suggests the deep read may be wrapping up. But there are more repos in dapptools. He could keep going.
The "compounding agility" framework (afternoon-sized fixes by deeply skilled people) could become a recurring reference if it resonates with the group.