A First-Principles-Driven Life

$[\![\textbf{缪}]\!]$: Miao's Search for Meaning

28 Nov 2023

Goodbye, the Waystation Crypto Libertarians!


It is Stephan Kinsella, one of the authoritative living minds in libertarian legal theory, who coined the term "waystation libertarians" as "Someone who was only 'passing through' libertarianism on their meandering, endless journey through ideologies and faddish, dilettantish interests."

As someone who has been in the libertarian movement actively for almost a decade and is currently retreating to a side project named "Mission: Liberty" due to time conflicts, I should like to have something to say about it.

Considering all other waystation libertarian movements during my time, Ron Paul libertarians, Sound Money (Goldgug or Bitcoin) libertarians, NatCon/Trump libertarians, Crypto libertarians, Covid libertarians, and now Milei libertarians, what I have been entangling my life the most is the sound money and crypto libertarians, where my startup is a business that surviving the post-SBF crypto winter. I have a growing grudge against the crypto libertarians, which I will explain in a moment. But similar to Mr. Kinsella's realization, I really should not.


I will put out my conclusion: the crypto libertarian movement is dead. If it's comforting to hear in a somewhat perverse way, its predecessor, the Sound Money/Bitcoin libertarians movement, is barely limping along, where the biggest hopes are that Blackrock can create an ETF and buy their digital gold! What an abomination!

In fairness, for the libertarian meetups I organize or go to, the Bitcoin movement remains the most activism-oriented and keeps bringing in new energy. Undoubtedly, Saifedean Ammous's works "The Bitcoin Standard" and "Principles of Economics" have inspired young minds to get into the idea of sound money, the Austrian school of thought in economics, and rational individualism.

But crypto libertarians, with its ever-moving targets (and some comical sagas along its way) - from native internet of money to stablecoins backed by fiat; from crowdfunding to VC-backed; from fully decentralized to sufficiently decentralized; from lemonade coin to decentralized physical infrastructure (DePIN!); from peer-to-peer decentralized operating systems (e.g. Urbit) to decentralized social (DeSo!); the list goes on - it is a rather elusive term that after a while, just like other waystation movements, it might have never existed!

So it is perhaps instead, the hope that it ever can exist is dead. In the face of this, anyone can justify their emotion. Anger: how we end up here where no one cares to promote our ideas. Sad and self-pity: how we didn't know better when we brought in detractors and fraudsters. Cynical: libertarian objectives will forever remain fringe. Jealous: look at the ones who joined and made the money; if I had the money, I should fund a movement myself!

There can be an indifferent emotion: it is just what it is. But it is a nonproductive emotion. While I believe the most practical and positive emotion and attitude to the matter is a subtle one. That is a skillful play between a rational acceptance and a wise realization of the libertarian movement under the historical context. Understanding the waystation phenomenon of movement is essential for anyone to come to this realization.

It is a relieving realization and practical attitude:


Congrats to Milei, and let us wholeheartedly welcome the upcoming Milei libertarians at our next stop!

Tags: politics
07 Jul 2023

A Solidity Compilers Collection in Nix

I have written solc.nix for a while, this article is a summary of it.

It is a collection of solidity compilers as nix expressions. While most solidity development toolchains (foundry, truffle, hardhat, etc.) have their bespoke mechanisms of downloading solidity compiler (solc) binaries, there are situations you would want not to rely on them:

If any of these cases apply to you, you should consider using solc.nix. Here are some tips on how.

Install Nix

Here is a write-up of why I think you should use Nix.

The fastest way to have it installed on your system is to follow the official instruction.

Once installed, you are good to go!

Using Nix Shell

If you just wanted to have some version of solc ad-hoc, you just need one simple line:

$ nix shell github:hellwolf/solc.nix#solc_0_4_26 github:hellwolf/solc.nix#solc_0_8_19

Then you will have two different solc binary available for you:

$ solc-0.4.26 --version
solc, the solidity compiler commandline interface
Version: 0.4.26+commit.4563c3fc.Linux.g++

$ solc-0.8.19 --version
solc, the solidity compiler commandline interface
Version: 0.8.19+commit.7dd6d404.Linux.g++

Using Nix Flake

If you want a deeper integration of it, you should consider using Nix Flake, here is an example of how to have multiple solc installed and one of them as the default one:

  inputs = {
    solc = {
      url = "github:hellwolf/solc.nix";
      inputs.nixpkgs.follows = "nixpkgs";

  outputs = { self, nixpkgs, solc }: let
    pkgs = import nixpkgs {
      system = "x86_64-linux";
      overlays = [
  in {
    devShell.x86_64-linux = with pkgs; mkShell {
      buildInputs = [
        (solc.mkDefault pkgs solc_0_8_19)


If you find this tool useful, please follow up at the git repo, contribute to keep this up to date, and share with people you might think it can help them too!

Tags: nix solidity
28 Feb 2023

My Haskell Tiny Game Jam Submissions: Othello & Lol

This post is a last-minute write-up for my participation in the Haskell Tiny Game Jam. The goal was to implement a game that fits into 10 lines of 80 characters. I have submitted two entries: one is a mini-othello game, the other is a meta game called "lol".

Mini Othello

It is a minimum Othello game implementation with MiniMax "AI" opponent using GHC 9.X with only the "prelude" module allowed.

The rule of the game is: "There are sixty-four identical game pieces called disks, which are light on one side and dark on the other. Players take turns placing disks on the board with their assigned color facing up. During a play, any disks of the opponent's color that are in a straight line and bounded by the disk just placed and another disk of the current player's color are turned over to the current player's color. The objective of the game is to have the majority of disks turned to display one's color when the last playable empty square is filled." (https://en.wikipedia.org/wiki/Reversi)

To play the game, checkout the tiny game repository and type ./play mini-othello:


I implemented the game entirely in minified code, so the variable and function names will be pretty cryptic!

Functions Overview

First, I represent the game state with a product type of Board and Side (only conceptually, but without actual code):

type Side = Black | White | None
type Coordinate = (Int, Int) -- from (0,0) to (7,7)
type Board = [Char] -- 64 of them!
-- | Game state type alias
type GameState = (Board, Side)

n = "_XO" -- visually they are reprensed these chars.

Then I implemented a helper function v for checking the number of flips per each line. There are 8 different directions the line can go, they are all enumerable by a StepCoordinate type.

-- number of steps (-1,0 or 1) in each direction to represent 8 different directions.
type StepCoordinate = (Int, Int)

-- | Check number of flips for one single direction
v :: GameState -> (GameState, Int) -> Coordinate -> StepCoordinate -> State
  -> (GameState, Int)
v savedGameState (currentGameState, nFlips) cor stepCor state
  -> (newGameState, nFlips)

Then the % operator for trial plays. Because this is used so often, the usage of this binary operator saves a lot of spaces!

(%) :: GameState -> Coordinate -> (GameState, Int)
(%) inGameState -> cor -> (outGameState, nFlips)

The q function moves the game state to the next round.

-- | Play and move to next round
q :: GameState -> Coordinate -> GameState
q currentGameState -> cor -> nextRoundGameState

The z function starts the game in the IO monad.

-- | Start game
z :: Side -> GameState -> IO ()
z humanSide (initialBoard,currentSide)

The e function is the game strategy for the AI:

-- | Strategy function!
e :: GameState -> GameState

It should use the q function to move the game state forward.

There are three game strategies attempted during the code jam:

  1. A naive strategy that finds the first possible move.
  2. A greedy strategy that finds the immediate move that flips the most pieces.
  3. A MiniMax strategy that tries to play the game with a few levels of depth with MiniMax decision rules.

I will enlist the code of (3) here. And because of its minified nature, it looks pretty hideous, I admit:

g h a=f(\c e->i e>i c?e$c)(-65*h,(0,0))$
        (\((b,p),d)->(h*(h*p==0?j a`c`i b$i$g(div h(-2))b),d))&
        (((,)=<<(a%))&k r);
e a=q a.j$g 4a

Here g 4a is a recursive function starting with 4, and it oscillates between positive ("max" stage) and negative ("min" stage) and halves at the same time (g -2a, then g 0a). This is a trick to use a single number to represent the stage and depth limit of the minimax process.

The evaluation function is kept simple; the more flips, the better. In a better strategy, certain pieces are worth more (at the corner or at the side).

Code Golfing Tricks

Compact Ternary Operator (Prelude)

If you use a lot of if then else, use this test?yes$no to save a lot of spaces:

infixr 1?;(b?x)y|b=x|0<3=y;

If you are not limited to using only "prelude" module, you might also use bool :: a -> a -> Bool -> a in base.

Shortest Function for "Other Side"

o=1:2:o, so that o 1=2 and o 2=1. Any other output is uninteresting!

This is a funny function enabled by Haskell's lazy evaluation semantics.

Shortest Cross Product Function

k=(<*>)=<<((,)<$>), so that:

  1. k[-1..1] is for interacting through all 8 directions ([1,1],[1,0],[1,-1],[0,1],[0,-1][-1,-1],[-1,1],[-1,0]), the 9th direction [0,0] is uninteresting and doesn't create trouble fur us.
  2. k[0..7] generates all 64 positions on the board for iterating through them.

The alternative would be using twice List applicative; this version saves spaces!

Make a move

This bizarre ternary function put a piece "w" at position "i" on the board "b":

(w^i)b=take i b++w:drop (i+1)b;

Other Ad-hoc One-Letter Functions

More tips about minifying and unminifying techniques can be found at: https://github.com/haskell-game/tiny-games-hs/issues/52

Lol - The Meta Game

My second submission to the game jam is less serious. In fact, it is a knock-off of the lolcat program! But it is tightly integrated with the game itself, hence a meta game!

The idea of it is straightforward: it pipes the "play" command to colorize its output and add additional text effects depending on how many "lols" you have inserted in the command line, e.g.:

./play lol lol lol lol lol ski would give you a rather trippy ski trip:


The trick of using the applicative list to generate an infinite list of variations:


The first parameter is the frequency of the rainbow color effects; the second one is the anis-terminal color code choice for foreground or background; the third one is additional text effects such as underlined texts and blinking texts.


I have enjoyed the journey, learned and jammed with various Haskell syntaxes for code golfing. Comparing to perl golf in the old days, code golfing in Haskell is a joy: if it type-checks, it usually works!

And I would like to thank the organizers, the participants, and all the fine folks at #haskell-game.

Tags: haskell game
27 Jan 2023

Reactive Exchanges - SLAMM dunk with Superfluid


Superfluid protocol enables money to move between entities continuously in time with no transaction. It is a new paradigm of how the money payment system works. Using this innovative building block, one can build novel applications for payroll, subscriptions, vesting, streaming AMMs, gaming, trade finance, rentals, and NFTs. Click here to read more what is Superfluid.

Though modeling a problem domain explicitly with time is not a novel concept. In fact, functional reactive programming (FRP) that was formulated in a ICFP 97 paper1 demonstrated that we can model animations elegantly and efficiently using continuous time. More than 20 years later, it was Superfluid, the first one that successfully connected the dots between FRP and the domain of money payment system, and has made it available on more than 6 EVM (Ethereum Virtual Machine) chains2.

In this article, we focus on one of its novel applications: different designs of exchanges that can facilitate swaps continuously in time3. By now, you should understand why I shall name them "Reactive Exchanges." Reactive exchanges are types of exchanges modeled with functional reactive programming.

The article expects you to be familiar with the trading terminologies and related innovations on decentralized ledgers. Axo trade whitepaper4, for instance, provides an excellent overview of this domain. Additionally, it would be best if you had an in-depth overview of what is Superfluid is and how it works5.

Different Reactiveness

Throughout the article, we separate the "reactiveness" of an exchange into three primary functions:

  • Contribution: does the input asset enter the exchange continuously in time?
  • Swap: does swap between the input and assets happen continuously in time?
  • Distribution: is output asset distributed continuously in time?

At the end of the article, we will then compare different designs using this classification.

Pseudo Reactive Exchange

The first attempt at having reactive exchange is making only the contribution continuous in time. The actual swaps are done through an external DEX (decentralized exchange), and distribution of the swap output is done through Superfluid IDA6, both happen periodically and are triggered by keepers ("cronjob at scale").

Let's call this "pseudo reactive exchange" since the actual swaps do not continuously in time. Here is a visualization of such exchange:


An example of such exchanges that is in production is Ricochet Exchange, an original and earliest example of how to build an unique exchange using Superfluid. It has been live for over an year, and provides an efficient way for anyone to DCA (dollar cost average)7 invest into on-chain assets.

Reactive Constant Function Market Maker (R-CFMM) Exchange

CFMM (Constant Function Market Maker) was the first class of AMM (Automatic Market Maker) applied to real-world financial markets4. In this article, we look into making one of the earliest and simplest versions of CFMM, Constant Liquidity Product (CLP) used in Uniswap v1 and v2 8 , 9, reactive.

To solve the equations required to make the CLP reactive, I used the python binding of the SageMath framework, a free open-source mathematics software system licensed under the GPL:

from sage.all import var, assume, function, solve

# let's define a handful of symbols
L_a, L_b, T, r, r_a, r_b, t_0, t = var("L_a", "L_b", "T", "r", "r_a", "r_b", "t_0", "t")
assume(t >= t_0)

First, let's define CLP:

from sage.all import var, assume, function, solve

def CLP(x, y, x_prime, y_prime):
    """Constant Liquidity Product Swap"""
    return x * y == x_prime * y_prime
assume(t >= t_0)

Secondly, let's try to use sage to solve the price function for selling asset A (sell amount \(a_\Delta\)) for asset B (get amount \(b_\Delta\)):

def solve_clp_a4b():
    print("# Solve CLP equation for selling A for B instantly\n")
    v_a = var("a_Δ")
    v_b = var("b_Δ")
    clp = CLP(
        L_a + v_a,
        L_b + v_b
    sols = solve(clp, v_b)
    assert(len(sols) == 1)

Sage spits out the well-known equation as expected:

\[L_{b\Delta} = \frac{L_b * a_\Delta}{L_a + a_\Delta}\]

Now it comes to how to make it reactive; two functions of time need to be solved: one for asset A and another for asset B. There are not enough free variables to solve two equations, but we do know that these two equations should be elated to each other. Here was the stroke of insight, and a magic variable "q" was introduced (and please don't ask more):

def solve_rclp_rtb_bidir():
    print("# Solve Reactive CLP rtb_bidir equation\n")
    cf_a = r_a * (t - t_0)
    cf_b = r_b * (t - t_0)

    q = var("q")
    clp = CLP(
        L_a + cf_a + q * cf_b,
        L_b + cf_b + 1/q * cf_a
    sols = solve(clp, q)
    print("L_{flowswap_a} =", (1/q * cf_a).subs(sols[0]))
    print("L_{flowswap_b} =", (q * cf_b).subs(sols[0]))

Gratefully, Sage spits out the magic formula we are looking for. Let's call it flowswap formula:

  1. \[L_{flowswap_a} = \frac{-(r_b * t_\Delta - L_b) * r_a * t_\Delta}{r_a * t_\Delta + L_a}\]
  2. \[L_{flowswap_b} = \frac{-(r_a * t_\Delta - L_a) * r_b * t_\Delta}{r_b * t_\Delta + L_b}\]

Here is an illustration of the global view of a reactive CLP exchange:


Another way of seeing it is from how swaps look after "reactification" on the chart of the CLP formula:


To make the exchange work for more participants, we again need to use IDA from Superfluid money, where the proportion of the flowswap output is determined by the market takers' continuous flow contributions.

Regarding "reactiveness, " market takers always "flowswap" constant flows for another non-linear flow of money, and they are both continuous in time and reactive. However, Superfluid money cannot distribute money through arbitrary formulas; that's why the distribution cannot be reactive unless a bespoke flowswap primitive is a built-in feature of the money.

You can find the work-in-progress prototype of this in the T-REX (Toy Reactive Exchange) Monorepo. If you are interested in making this closer to production, join Superfluid's wave pool program.

Zero-Intermediate-Liquidity Market Maker (ZILMM) Exchange

Another fascinating and unique variation of AMM enabled by Superfluid money is a market maker that requires zero intermediate liquidity, where liquidity goes from LPs to traders directly as constant flows. As a result, a trade-off (or feature) is that such exchanges may not easily facilitate instant swaps.

I shall call such exchange ZILMM: Zero-Intermediate-Liquidity Market Makers. The pioneer of such design is Superfluid's ecosystem project Aqueduct. We will leave the reader to discover more about the project on their own, and we will only include an illustration from them to get a taste of it:

  1. At market liquidity "equilibrium"

  2. Traders tilting the "equilibrium"


Since all flows involved are constant flows, it should not be a surprise that this design of the reactive exchange ticks all the reactiveness boxes!


Let us review the "reactiveness" of all three Superfluid money enabled market maker designs:

Exchange Type Contribution Swap Distribution Known
/ Reactiveness Reactiveness Reactiveness Reactiveness Projects
Pseudo Yes No No Ricochet
R-CFMM Yes Yes Maybe(a) T-Rex
ZILMM Yes Yes Yes Aqueduct


a) Distribution with the complex formula required by R-CFMM is viable but not necessarily desirable.

Each design has its pros and cons, and it is beyond the purpose of this article to dive into them.

However, Superfluid money demonstrably enlarged the design surface of AMM. I believe it should be a safe bet that innovations enabled by Superfluid money are not restricted to AMM and other types of financial contracts such as options, futures, interest rate swaps, etc., should have unique Superfluid money enabled designs too.

While reactive exchanges accurately reflects the underlying technology, a more playful term could be SLAMM (Streaming Liquidity Automatic Market Makers). I will end the article with a slogan:

Let's SLAMM dunk it with Superfluid.



Elliott, Conal; Hudak, Paul. "Functional Reactive Animation". Functional Reactive Animation. ICFP ’97. Retrieve 14 July 2018.


All deployments of Superfluid protocol can be accessed through Superfluid app.


It should not to be confused with the existing financial term continuous trading.


Uniswap v1 Protocol Hayden Adams et al. Novembrer 2018. URL: https://docs.uniswap.org/protocol/V1/introduction. Accessed on 15/10/2021.


Uniswap v2 Core Hayden Adams et al. March 2020. URL: https://uniswap.org/ whitepaper.pdf. Accessed on 15/10/2021.

Tags: superfluid defi semantic money
11 Dec 2022

Stop Writing Instructions and Use Nix

If you are still writing development environment setup instruction for your project, time to use Nix!

With Nix:

More over, you should use Nix Flakes. This upcoming new feature of Nix will power-up your build environment with a lock file that is similar to your yarn.lock/package-json.lock/Cargo.lock/etc., but for all types of projects.

It is a experience changer for many engineering organizations, e.g. at shopify.

Personally, each time I want to contribute to an open source project, the first thing I would do is to add a flake.nix to their project and decorate their CONTRIBUTING.md with a much more concise development environment setup using Nix section.

For more reasons why you should use Nix, I recommend these readings:

After you are "nix pilled" inevitably, Nix official website has a pretty decent collection of learning materials from getting started to comprehensive references.

There are also some good materials done by the community:

Further more, Nix Flakes have also its own interesting materials to read:

Happy Nix!

Tags: nix devops
Other posts
Creative Commons License
A First-Principles-Driven Life by Miao, ZhiCheng is licensed under a Creative Commons Attribution 3.0 License.