INTERN

From empty repo to verified mainnet.

I write the contracts. Then the app that talks to them. Then the indexer that feeds the app. Five years of this, since 2020. Most devs do one layer and hand you the glue work. I do all three, so nothing falls between them.

discipline
Solidity · Yul · TS
experience
5 yrs / since 2020
engagement
contract · 0→1 build
status
taking work
local
--:--:-- UTC
01

What I take on

scope → mainnet, one operator

Hire me for the parts that kill launches: the money math, the access control, and the systems that have to keep serving traffic when the chart goes vertical.

Contracts

Solidity that doesn't get drained. Bonding curves, AMM pools, order books, launchpads, fee vaults, vesting. Assembly on the hot paths where gas is the product.

foundry · yul · openzeppelin · slither · echidna

Testing

Fuzz and invariant suites, not happy paths. I write the property that must never break (curve monotonicity, solvency, fee conservation) and let the fuzzer go hunt a counterexample.

forge fuzz · invariants · fork tests · gas snapshots

Frontend

Apps that hold up under load. Wallet flows, multicall batching, optimistic UI, live charts off websocket streams. ABIs typed end to end, so the app can't call a function that isn't there.

next.js · typescript · wagmi · viem · tanstack

Indexers

The backend nobody volunteers for. Event indexers that survive reorgs, cursor checkpointing, idempotent writes, backfills that don't double count. Postgres, tuned.

node · postgres · redis · websockets · queues

Launch infra

Deploy scripts, verification, monitoring. Deterministic deploys, explorer verified source, seeded liquidity, alerts on the invariants that matter once real money is in.

ci/cd · testnet→mainnet · verify · alerting

Positioning

I ran comms before I wrote code. I know how attention moves on this timeline. I build the product so the story and the mechanics say the same thing.

launch strategy · narrative · community
02

A working EVM, in this page

no libraries · no server · ~300 lines

Anyone can put "Solidity" on a page. So here is an EVM interpreter I wrote for this site. Assembler, stack, memory, storage, gas. Step it and watch real bytecode run. The default program is constant product swap math, the same arithmetic that decides what your buyer gets and what your treasury keeps.

ready 0 bytes
pc
0
gas
0
depth
0
mem
0
assembled. press step.
03

The hard parts

one sample per layer

Four pieces of production code. The ones where being wrong costs money, and where most repos quietly copy something they can't derive.

contracts · EIP-712 signature claim, entirely in assembly
// transient reentrancy lock, low-s guard, hand-rolled ecrecover, // O(1) storage-bitmap replay guard. zero abstraction overhead. function claim(uint256 id, uint256 amt, uint8 v, bytes32 r, bytes32 s) external { assembly { // EIP-2: reject malleable high-s signatures if gt(s, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { mstore(0x00, 0x8baa579f) revert(0x1c, 0x04) // InvalidSig() } // EIP-1153 transient reentrancy lock if tload(_LOCK) { mstore(0x00, 0xab143c06) revert(0x1c, 0x04) } tstore(_LOCK, 1) let p := mload(0x40) mstore(p, _CLAIM_TYPEHASH) mstore(add(p, 0x20), caller()) mstore(add(p, 0x40), id) mstore(add(p, 0x60), amt) let structHash := keccak256(p, 0x80) mstore(p, hex"1901") // 0x19 0x01 ‖ domainSep ‖ struct mstore(add(p, 0x02), _DOMAIN_SEPARATOR) mstore(add(p, 0x22), structHash) let digest := keccak256(p, 0x42) mstore(p, digest) mstore(add(p, 0x20), and(v, 0xff)) mstore(add(p, 0x40), r) mstore(add(p, 0x60), s) pop(staticcall(gas(), 0x01, p, 0x80, 0x00, 0x20)) // ecrecover if iszero(eq(mload(0x00), sload(_SIGNER))) { mstore(0x00, 0x8baa579f) revert(0x1c, 0x04) } // O(1) replay guard: one storage bit per id mstore(0x00, shr(0x08, id)) mstore(0x20, _CLAIMED_SLOT) let bucket := keccak256(0x00, 0x40) let bit := shl(and(id, 0xff), 1) if and(sload(bucket), bit) { mstore(0x00, 0x646cf558) revert(0x1c, 0x04) } sstore(bucket, or(sload(bucket), bit)) tstore(_LOCK, 0) } _mint(msg.sender, id, amt, ""); }
contracts · 512-bit mulDiv, the function 90% can copy but can't derive
// floor(a · b / d) at full 512-bit precision. no overflow, no lost bits. function mulDiv(uint256 a, uint256 b, uint256 d) internal pure returns (uint256 r) { uint256 prod0; // low 256 bits of a * b uint256 prod1; // high 256 bits of a * b assembly { let mm := mulmod(a, b, not(0)) prod0 := mul(a, b) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } if (prod1 == 0) return prod0 / d; // fits in 256 bits require(d > prod1); // otherwise the result overflows uint256 rem; assembly { rem := mulmod(a, b, d) prod1 := sub(prod1, gt(rem, prod0)) prod0 := sub(prod0, rem) } uint256 twos = d & (~d + 1); // isolate lowest set bit of d assembly { d := div(d, twos) prod0 := div(prod0, twos) twos := add(div(sub(0, twos), twos), 1) } prod0 |= prod1 * twos; // Newton-Raphson: invert d mod 2^256, doubling precision each step uint256 inv = (3 * d) ^ 2; inv *= 2 - d * inv; inv *= 2 - d * inv; // 2^8 -> 2^16 inv *= 2 - d * inv; inv *= 2 - d * inv; // 2^32 -> 2^64 inv *= 2 - d * inv; inv *= 2 - d * inv; // 2^128 -> 2^256 r = prod0 * inv; // exact, one multiply, no division }
frontend · optimistic tx lifecycle that survives reorgs and stale state
// simulate -> write -> reorg-tolerant wait -> reconcile -> roll back. // what breaks in prod: stale closures and out-of-order receipts. export function useMint<const TAbi extends Abi>(config: MintConfig<TAbi>) { const client = usePublicClient(); const { data: wallet } = useWalletClient(); const qc = useQueryClient(); return useMutation({ mutationFn: async (args: MintArgs) => { // simulate first, so reverts surface in the UI, not the chain const { request } = await client.simulateContract({ ...config, account: wallet!.account, args: [args.qty, args.proof], }); const hash = await wallet!.writeContract(request); const receipt = await client.waitForTransactionReceipt({ hash, confirmations: 2, // ride out shallow reorgs }); if (receipt.status === "reverted") throw new TxReverted(hash); return receipt; }, onMutate: async (args) => { await qc.cancelQueries({ queryKey: ["supply"] }); const prev = qc.getQueryData<bigint>(["supply"]); qc.setQueryData(["supply"], (s = 0n) => s + BigInt(args.qty)); return { prev }; // rollback context for onError }, onError: (_e, _a, ctx) => qc.setQueryData(["supply"], ctx?.prev), onSettled: () => qc.invalidateQueries({ queryKey: ["supply"] }), }); }
infra · reorg-aware indexer, the bug that silently corrupts prod
// on a reorg you must roll state back, not just append the new head. async function indexLoop(db: DB, client: PublicClient) { let cursor = await db.lastBlock(); // { number, hash } for (;;) { const head = await client.getBlockNumber(); if (head <= cursor.number) { await sleep(2_000); continue; } const next = await client.getBlock({ blockNumber: cursor.number + 1n }); // parent hash mismatch => a reorg happened at or below the cursor if (next.parentHash !== cursor.hash) { const fork = await findCommonAncestor(db, client, cursor); await db.rollbackTo(fork.number); // drop orphaned rows cursor = fork; continue; // re-walk from the fork point } const logs = await client.getLogs({ blockHash: next.hash, events: WATCHED }); await db.transaction(async (tx) => { // atomic: logs + cursor together await tx.insertLogs(decode(logs)); await tx.setCursor({ number: next.number, hash: next.hash }); }); cursor = { number: next.number, hash: next.hash }; } }
04

Launch math, live

x · y = k · 30 bps

Drag the trade size. This is the curve your buyers actually hit. The slippage they eat, the fee your LPs earn, and the depth you need so one wallet can't move your price 40%. Pool depth is the number founders get wrong most often.

constant product · uniswap-v2 semantics k = --
reserves--
spot price--
you receive--
effective--
price impact--
lp fee--
05

How working with me goes

no agency layer
  1. Scope call

    You describe the mechanism. I tell you what's hard about it, what it costs in gas, and where it can be attacked. That happens before anyone signs anything.

  2. Contracts and properties first

    The money layer gets built and attacked before a single pixel exists. If the invariants don't hold, nothing downstream matters.

  3. App and indexer

    Typed against the real ABI, wired to a reorg safe indexer, deployed behind CI. You watch it work on testnet, not in a status update.

  4. Mainnet, verified

    Deterministic deploy, source verified on the explorer, alerts on the invariants. You get the keys and the repo. Full ownership, no lock in.

team sizejust me
handoffsnone
chainsany EVM
testingfuzz + invariant
deliveryverified on explorer
ownershipyour repo, your keys
meetingsas few as possible

I work best as the technical cofounder you don't have yet. Give me a mechanism and a deadline, I go quiet, and I come back with something deployed.

06

Stack

what I reach for
chain
solidity · yul · foundry · hardhat · openzeppelin · slither · echidna
client
viem · wagmi · rainbowkit · ethers · lightweight-charts
app
next.js · react · typescript · tailwind · tanstack query
data
postgres · neon · redis · prisma · websockets · queues
ops
docker · railway · vercel · github actions · irys · ipfs

If you have a mechanism and a deadline, send it.

Tell me what you're building and when it has to be live. If it's a fit I'll tell you how I'd build it in the first reply. If it isn't, I'll say so instead of billing you to find out.