Rainforest Cheng
Back to portfolio

Case study

HashgreenSwap

Frontend Engineer · Hashgreen Labs · 2023 – Present

The AMM swap interface for Chia CAT tokens, built on the Project Pyke exchange backend.

HashgreenSwap swap-confirm flow with the swap summary and the Hoogii wallet signature request

Swap confirm & wallet signature

Swapping on a logarithmic curve

Type an amount and get a live quote — minimum received, price impact, and fee — in either direction, exact-in or exact-out.

I built the swap engine. I ported Pyke's AMM pricing out of the implementation paper into one pure function and wired both quote directions — exact-out inverts the same invariant, it isn't a second approximation.

Pyke is not constant-product (x·y=k) — calcUserSwap in utils/swap.ts solves a log-space invariant with a pool fee φ and a safety margin ε = 1e-5; price impact is |1 − actual/spot|. The function runs client-side on every keystroke.

slippage
Rate
1 XCH ≈ 0.3800 hUSDC
Minimum received
362.4656 hUSDC
Price impact
4.13%
Liquidity fee (300 bps)
30.0000 XCH

Mirrors src/utils/swap.ts · src/hooks/useSwapQuote.ts

Sign once, settle on-chain

Execute with no centralized order book: the browser assembles a signed Chia offer, the chain settles it, and the UI tracks it to confirmation.

I unified three wallet backends behind one `IWallet.createOffer` interface — Goby, Hoogii, and Chia over WalletConnect — so the app never knows which wallet signed, and I built the recent-transaction tracker on top.

A swap is a Chia offer signed with BLS keys in the wallet; the CLVM puzzle-reveal bundle is built client-side, then the offer walks VALID → IN_MEMPOOL → ON_CHAININVALID means a coin it spent was used elsewhere first.

offerStatusEnum.PENDING

Connect a wallet to sign the offer.

Mirrors src/wallets/IWallet.ts · src/components/RecentTransactions.tsx

One-sided liquidity, pool-share math

Add liquidity — including Zap, a single-asset deposit — with share-of-pool and LP minted shown as you type.

I built the liquidity forms and the paired-input hook that locks the two deposits to the pool ratio, then the Zap toggle that collapses them into one input for single-sided deposits.

In balanced mode useAssetInputPair pins the other side to the reserve ratio; Zap converts part of one asset before minting — which is why the backend counts add_liquidity and add_liquidity_zap as distinct tx types.

Zap · single-sided deposit
Share of pool
5.7471%
LP received
76,219.51 LP
Pool ratio
1 XCH : 0.3800 hUSDC
add_liquidityBalanced — the second input is pinned to the reserve ratio.

Mirrors src/hooks/useAssetInputPair.ts · src/components/LiquidityPanel.tsx

Shipping it: one image, four environments

One container image is promoted through sandbox → UAT → staging → prod on Kubernetes, each with its own config and autoscaling.

I owned delivery — the multi-stage Dockerfile that ships Next's standalone output as a non-root container, the Helm chart with per-env values, and the GitHub Actions that build, push, and helm-upgrade.

The build is base → deps → builder → runner on node:20-alpine; an ARG selects .env.{uat|stg|sandbox|prod}, output: 'standalone' keeps the image tiny, and the runner drops to uid 1001.

image:
  repository: registry.hashgreen.net/pyke-swap
  tag: sha-3f9c1a2
replicaCount: 2
ingress:
  host: swap.uat.hashgreen.net
autoscaling:
  minReplicas: 2
  maxReplicas: 4
  targetCPUUtilizationPercentage: 70
HPA 2–4 pods · target 70% CPU
idle — pick an environment and deploy.
TVL
$780k
All-time volume
$731k
Transactions
21,494
swap 11,149 · add_liquidity 6,867 · zap 1,621 · remove 3,478

Mirrors Dockerfile · chart/values.{env}.yaml · .github/workflows/deploy.yml

One library, three languages

Every page reuses the same primitives and tokens, and the UI switches EN / Simplified / Traditional Chinese at runtime — no reload.

I built the shared `@hashgreen/ui-libraries` package the app consumes, and wired `next-i18next` with a chained backend so a language flips in place without a round-trip.

Language runs through i18next-chained-backend — localStorage first, HTTP second — with per-namespace JSON in public/locales/{en,zh-CN,zh-TW}, and the color and text tokens live once in the shared package.

dark
Swap summary
You pay
1,000.00 XCH
You receive
364.29 hUSDC
Minimum received
362.47 hUSDC
Price impact
4.13%
Liquidity fee
30.00 XCH
Est. value$364.29

Mirrors libs/ui-libraries/pyke · next-i18next.config.js · public/locales/