Integration guide

LONGFOLIO
bot & API integration

← back to PORTFOLIO · plain-text version

Network: Robinhood Chain, chainId 4663. Public RPC: https://rpc.mainnet.chain.robinhood.com.

Addresses

ContractAddress
LONGFOLIO (the coin, 18 decimals)0x9A64C06795b254C8aA70086E9FDC0FBE136F0110
Zap / hook (buy & sell entry point)0x04D9d2cdacDf8E95484eC787E40479B109Bb6888
L8NG basket token0xE607Ce5825517c0581fb22CB635578b28c2A62b2
Uniswap V4 PoolManager0x8366a39CC670B4001A1121B8F6A443A643e40951
Universal Router0x8876789976decbfcbbbe364623c63652db8c0904
Permit20x000000000022D473030F116dDEE9F6B43aC78BA3
V4 Quoter0x8dc178efb8111bb0973dd9d722ebeff267c98f94

The pool (Uniswap V4, custom curve via hook)

PoolKey {
  currency0:   0x0000000000000000000000000000000000000000   // native ETH
  currency1:   0x9A64C06795b254C8aA70086E9FDC0FBE136F0110   // LONGFOLIO
  fee:         0
  tickSpacing: 1
  hooks:       0x04D9d2cdacDf8E95484eC787E40479B109Bb6888
}
poolId = 0xd2adda941c977b675e4a60bba17e84e45ccd110913e1ec206a46da2c1895314d

The pool holds no liquidity of its own. The hook does the whole trade inside beforeSwap: ETH → the 8 basket tokens → mints L8NG → swaps L8NG for LONGFOLIO on the Long pool, and hands the coin back through the PoolManager. Consequences for integrators:

Quote

V4Quoter.quoteExactInputSingle((PoolKey, bool zeroForOne, uint128 exactAmount, bytes hookData))(uint256 amountOut, uint256 gasEstimate). Selector 0xaa9d21cb. Buy: zeroForOne = true, exactAmount = wei of ETH, hookData = 0x. Sell: zeroForOne = false, exactAmount = LONGFOLIO wei. Call it with eth_call (it is not declared view, but it returns normally).

Buy with ETH — Universal Router (execute(bytes commands, bytes[] inputs, uint256 deadline), selector 0x3593564c, send the ETH as value)

commands = 0x10                                   // V4_SWAP
actions  = [0x06, 0x0c, 0x0e]                    // SWAP_EXACT_IN_SINGLE, SETTLE_ALL, TAKE
params[0] (SWAP_EXACT_IN_SINGLE) = abi.encode((PoolKey, true, uint128 amountIn, uint128 minOut, bytes ""))
params[1] (SETTLE_ALL)           = abi.encode(address(0), uint256 amountIn)
params[2] (TAKE)                 = abi.encode(LONGFOLIO, address recipient, uint256 0)   // 0 = take the full credit
inputs[0] = abi.encode(actions, params)

viem example:

const KEY = { currency0: "0x0000000000000000000000000000000000000000", currency1: "0x9A64C06795b254C8aA70086E9FDC0FBE136F0110", fee: 0, tickSpacing: 1, hooks: "0x04D9d2cdacDf8E95484eC787E40479B109Bb6888" };
const KEY_T = { type: "tuple", components: [{name:"currency0",type:"address"},{name:"currency1",type:"address"},{name:"fee",type:"uint24"},{name:"tickSpacing",type:"int24"},{name:"hooks",type:"address"}] };
const swap = encodeAbiParameters([{ type: "tuple", components: [KEY_T, {type:"bool"},{type:"uint128"},{type:"uint128"},{type:"bytes"}] }], [[KEY, true, amountIn, minOut, "0x"]]);
const settle = encodeAbiParameters([{type:"address"},{type:"uint256"}], ["0x0000000000000000000000000000000000000000", amountIn]);
const take = encodeAbiParameters([{type:"address"},{type:"address"},{type:"uint256"}], [KEY.currency1, recipient, 0n]);
const input = encodeAbiParameters([{type:"bytes"},{type:"bytes[]"}], ["0x060c0e", [swap, settle, take]]);
await router.write.execute(["0x10", [input], deadline], { value: amountIn, gas: 6_000_000n });

Direct PoolManager integration: unlockswap(key, {zeroForOne: true, amountSpecified: -int256(amountIn), sqrtPriceLimitX96: 4295128740}, "")settle the ETH, take the coin. Sell: zeroForOne = false, limit 1461446703485210103287273052203988822378723970341.

Sell — two options

  1. Universal Router, same structure with zeroForOne = false, SETTLE_ALL(LONGFOLIO, amountIn) and TAKE(address(0), recipient, 0). Needs LONGFOLIO.approve(Permit2, max) and Permit2.approve(LONGFOLIO, UniversalRouter, amount, expiration) (selector 0x299ec9ee).
  2. Zap directly: sell(uint256 coinIn, uint256 minEthOut, uint256 deadline) (selector 0xd3c9727c) after LONGFOLIO.approve(zap, amount). ETH is sent to the caller.

Simplest buy path (no router): the zap

buy(uint256 minCoinOut, uint256 deadline) payable, selector 0xd6febde8, on 0x04D9d2cdacDf8E95484eC787E40479B109Bb6888. Send ETH as value; the coin goes to msg.sender. Gas ≈ 5M.

Price, trades and volume

Eventtopic0
HookBought(address indexed router, uint256 ethIn, uint256 basketMinted, uint256 coinOut) — buys through the PoolManager / any router0x18da56f4ab10a2ade66d651ee18224cfa82d3581722faa4e401270e41ac3a66f
HookSold(address indexed router, uint256 coinIn, uint256 basketOut, uint256 ethOut)0xb315275ffd13ef67d85701c2113ee48e84997f7f67c8f91ab5d38435c2c731de
Bought(address indexed user, uint256 ethIn, uint256 basketMinted, uint256 coinOut, uint256[] refunded)zap.buy0x41c6893c9c9b0ea553f4bc3bd6586a0adfe88ac8a5301b1d7cef460799197f6d
Sold(address indexed user, uint256 coinIn, uint256 basketOut, uint256 ethOut)zap.sell0xe029f26dbcf8c42dd2f352c10214a7fc26773dc62482c6241334a0402ac09a80

Reverts you may see

SelectorMeaningWhat to do
0x90bfb865 WrappedError(...)the hook reverted; the inner selector is insidedecode the inner reason
0x590f58a9 PartialFill(member, hop, consumed, wanted)one basket leg's pool could not absorb the legretry with a smaller amount
0x2746152a Slippage(got, min)minOut not metre-quote
0x203d82d8 Expired()deadline passednew deadline
out of gas at ~1Mgas limit too low6M gas limit

Notes: buys through a router may return small amounts of basket member tokens to tx.origin (leftovers above 0.5% of a leg are refunded to the buyer). Every trade pays the 2.1% pool fee plus the basket routes (≈1.8%); there is no anti-snipe fee anymore.

Questions: @portf0110 on X