Documentation

The Greenwood field guide.

How launches, pools and fees work, which contracts run them, and how to read and trade every token from your own code.

At a glanceLiquidity burned at launch
0Launch feegas only
0 %Fee per swapfixed, on buys and sells
0 %Of every fee to the creatorfor the life of the pool
0.0BFixed supplyno mint, no tax
Chapter 01

Overview

Greenwood is a meme-token launchpad on Robinhood Chain, an Arbitrum Orbit L2 where the gas token is ETH. One transaction (GreenwoodLaunchpad.launch()) deploys a fixed-supply ERC-20, opens a TOKEN / ETH Uniswap v4 pool with the Greenwood hook, puts 100 % of the supply in it and mints the LP position to 0x…dEaD. Nobody can ever pull the liquidity.

There is no configurable tax: every swap pays a fixed 1 % hook fee. FEE_BPS = 100 is a constant and the hook has no owner, so the rate can never change. The fee is split 80 % to the launch’s fees recipient (the creator by default) and 20 % to the protocol.

One-click launch

Deploy, v4 pool, burned LP and an optional first buy, in one atomic transaction.

Liquidity can’t be pulled

The LP position is minted to 0x…dEaD at launch: nobody, creator or Greenwood, can withdraw it. It does not cap the creator’s first buy.

Fixed 1 % fee

A constant in the hook, the same on buys and sells. It never changes.

80 % to creators

On every swap, for the life of the pool, paid in the token and in ETH.

Plain ERC-20

No tax, no mint, no proxy. Ownership is renounced in the constructor.

Live on Uniswap from block one

No bonding curve, no graduation: the token trades on its final v4 pool right away.

$WOOD is Greenwood’s official platform token, pinned on the board.

Chapter 02

Robinhood Chain

Greenwood runs on Robinhood Chain, an EVM-compatible Arbitrum Orbit L2. Any wallet that supports custom networks works, and gas is paid in ETH.

Mainnet chain ID
5042
rpc.mainnet.arc.io · explorer.arc.io · arc.etherscan.io
Testnet chain ID
5042002
rpc.testnet.arc.io · explorer.testnet.arc.io
This app targets
Robinhood Chain
chain ID 4663 · rpc.mainnet.chain.robinhood.com
Native gas token
ETH
18 decimals, sent as msg.value
Base fee
≈ 20 gwei
a launch costs a few cents in gas
Explorers
Blockscout · Etherscan
robinhoodchain.blockscout.com for this app · arc.etherscan.io on mainnet only
Uniswap v4
PoolManager 0x8366a3…e40951
same address on mainnet and testnet · every pool lives here

Pools pair against ETH

Every Greenwood pool uses ETH: currency0 = address(0), 18 decimals. It always sorts first, so the token is always currency1. The ERC-20 view of the same funds at 0x3600…0000 (6 decimals) is not used by any pool. If you integrate, never mix the two decimal bases.
Chapter 03

Quote token: ETH

Every Greenwood token trades against a single quote: ETH. Nothing is chosen at launch, and there are no other quote assets.

ETH
address(0) · 18 dec
the only quote: every Greenwood pool is TOKEN / ETH
ETH ERC-20 view
0x3600…0000 · 6 dec
same balance, not used by pools
Buying needs no approval: ETH is sent as the transaction value (msg.value), and the launch’s first buy is msg.value too. Only sells need an approval: the token to Permit2 once, then Permit2 to the UniversalRouter, which the app grants for 30 days.
Chapter 04

Launching a token

From Create, fill in a name, ticker, description and logo, choose a starting market cap, optionally add a first buy, and sign once. The app calls GreenwoodLaunchpad.launch(LaunchParams) (payable). One atomic transaction does everything:

  1. 1Deploy a full GreenwoodToken contract (not a clone) via CREATE2. Its constructor mints the whole supply to the launchpad and renounces ownership.
  2. 2Initialise the Uniswap v4 pool: ETH / token, LP fee 0, tick spacing 200, hooks = LaunchHook, at the start tick of the chosen market cap. A pool that is already initialised makes the launch revert.
  3. 3Hand the pool’s fees-recipient role in the hook to your feesRecipient (address(0) = you), before any swap can pay a fee.
  4. 4Mint a single-sided, token-only position over [minUsableTick, startTick] holding 100 % of the supply, straight to 0x…dEaD: burned.
  5. 5Optional first buy with msg.value through the UniversalRouter, tokens sent to your wallet.
  6. 6Refund the rounding dust, record the Launch and emit Launched.
Launch fee
None
gas only, paid in ETH: a few cents, a bit more with a large on-chain logo
Token supply
1,000,000,000
every launch made from Greenwood · all of it in the pool
Decimals
18
standard ERC-20
LP
Burned to 0x…dEaD
cannot be retrieved, ever
Token address
CREATE2, not predictable
salt mixes your random bytes, your address, the block number and a nonce
Mintable
No
owner() = 0x0 from block one
Metadata
On-chain JSON ≤ 2,048 bytes
MAX_METADATA_BYTES, enforced by the contract
Name / ticker
≤ 32 chars / 1–8 [A-Z0-9]
description ≤ 256 chars (create form)
Logo
≤ 5 MB upload
cropped square, re-encoded in your browser to ≤ 96 px WebP / JPEG
First buy
Optional, in ETH
tokens to your wallet · keep a little ETH aside for gas
Fees recipient
Your wallet by default
any address · it can hand the role on later
“100 % of the supply in the pool” is exact up to a few wei: liquidity is rounded down, and the dust is refunded to the creator in the same transaction.

Starting market cap

The starting market cap sets the pool’s opening price. The start tick is the multiple of 200 closest to that price, so the real figure lands within about 1 % of the preset.

$2,500
tick 129,000
≈ 400,054 tokens per ETH · start ≈ $2,499.66
$5,000
tick 122,000
default · ≈ 198,668 tokens per ETH · start ≈ $5,033.52
$10,000
tick 115,200
≈ 100,652 tokens per ETH · start ≈ $9,935.22
$25,000
tick 106,000
≈ 40,114 tokens per ETH · start ≈ $24,929.22
// token = currency1, ETH = currency0 (both 18 decimals)
tokensPerUsdc(tick) = 1.0001 ^ tick
startTick           = round(log(supply / marketCapUsd) / log(1.0001) / 200) * 200
marketCap(tick)     = supply / 1.0001 ^ tick
marketCap(sqrtP)    = supply / (sqrtPriceX96 / 2^96)^2

The token contract

Every launch deploys the same contract, GreenwoodToken: an OpenZeppelin ERC-20 whose supply is minted once, in its constructor. It is a real contract at the token address, not a proxy: there is no implementation to swap and nothing that can mint.

Standard
ERC-20 (OpenZeppelin)
18 decimals · no EIP-2612 permit
Mint / burn
No
supply created once, in the constructor
Transfer tax
No
transfers move exactly what you send
Blacklist / pause
No
no admin functions at all
Proxy / upgrade
No
full contract, immutable
Owner
0x0
Ownable, renounced in the constructor
Source verification
Robinhood Chain mainnet only, best-effort
on arc.etherscan.io (Etherscan API v2), retried when the token page opens · Etherscan doesn’t support Robinhood Chain Testnet

Why the token address is not predictable

Robinhood Chain has no on-chain randomness (prevrandao is always 0). If a token address could be computed in advance, someone could initialise its v4 pool first, at another price, and make the launch revert. So the CREATE2 salt mixes 32 random bytes from your browser with your address, the block number and a nonce. The address stays unknowable until the transaction is public, and a reverted launch never burns an address: retry with a fresh salt.
Chapter 05

Fee model

Fixed

Greenwood has no configurable tax and no allocation buckets. Every pool carries LaunchHook, which takes a fixed 1 % on every swap and books it for the pool. The rate is a constant in the hook’s code and the hook has no owner.

Swap fee
1 % (FEE_BPS = 100)
constant, no owner, can never change
Applies to
Buys and sells
same rate both ways
Charged on
The swap’s unspecified side
exact-in buy: in tokens, from the output · exact-in sell: in ETH, from the output
Exact-output swaps
Added to the input
integrators only: the app always swaps exact-in
Pool LP fee
0
an LP fee would accrue to the burned position
Creator share
80 %
to the pool’s fees recipient
Protocol share
20 %
both sides → TREASURY (hook immutable)
Token transfer tax
None
GreenwoodToken is a plain ERC-20

What the hook does

afterSwapbooks the fee

Takes 1 % of the unspecified side from the PoolManager and books it per pool (owed0 ETH, owed1 token). It pays no one during the swap, so no recipient can ever make a swap revert.

collectFeessplits 80 / 20

Anyone can call collectFees(poolKey): 80 % goes to the fees recipient, 20 % to the protocol. The caller can’t redirect anything.

beforeSwapfeeds the TWAP

Folds the pre-swap tick into an on-chain TWAP accumulator, at most once per block, keeping at most one checkpoint per clock minute.

The hook has no liquidity permission: it can neither block nor skim a liquidity add or remove. Its only effect on a swap is the 1 %.

Where the protocol’s 20 % goes

The hook pays the whole protocol share, both the token side and the ETH side, to one immutable address: TREASURY. Greenwood’s deploy script sets it to the treasury, the deployer’s address. No part of it is paid to token holders. On the hook linked on this page, TREASURY is 0xfd08…6289.
Chapter 06

Launch ordering, no snipe window

Greenwood has no anti-snipe machinery. The only thing it guarantees is ordering: the creator’s optional first buy runs inside the launch transaction, right after the pool is created, so nobody can buy before it.

That first buy has no cap. A creator can buy any share of the supply at the launch price, before anyone else, and sell it to later buyers. Burned liquidity does not prevent that: before you buy, look at the launch transaction and the creator’s balance.

Snipe window
None
anyone can buy from the next transaction on
Max buy per tx
None
no max-wallet, cooldown, blacklist or whitelist · creator included
Creator first buy
Same tx as pool creation, uncapped
nobody can buy before it · amountOutMinimum 0 is safe because it is atomic · it pays the 1 % like any swap
Token address
Unknowable until the tx is public
Robinhood Chain has no prevrandao: the salt mixes caller entropy, block number and a nonce
Pre-initialised pool
Launch reverts
retry with a fresh salt, no address is burned
Sell floor
Launch price
no ETH liquidity below it: sells never fill under it
Presale / reserved tokens
None
100 % of the supply goes into the pool · the creator’s uncapped first buy is not a reservation, but it buys first
There is no snipe window, cooldown or per-wallet cap, for bots or for the creator. Bots can buy right after your launch like anyone else. What Greenwood guarantees is only that the creator’s first buy, of any size, lands first, in the launch transaction itself.
Chapter 07

Trading

Each token page has a swap panel that routes through Uniswap’s UniversalRouter and quotes with the V4Quoter. The pool is a standard Uniswap v4 pool, so any v4-aware wallet, aggregator or bot can trade it too.

AMM
Uniswap v4
one concentrated range, from the start price to the top of the price scale
Quote token
ETH
currency0 = address(0)
Starting price
Start market-cap preset
a tick, multiple of 200
LP fee
0 %
the hook’s fixed 1 % is the only fee
Tick spacing
200
Router
UniversalRouter
V4_SWAP · exact-in single hop
Quote
V4Quoter.quoteExactInputSingle
runs the hook: amountOut is net of the 1 %
Slippage
1 % · 3 % · 5 %
3 % by default, minOut applied to the quote
Deadline
10 minutes
Buys
One transaction, no approval
ETH sent as msg.value · Max keeps a little ETH for gas
Sells
Permit2
token → Permit2 once per token, then a 30-day router allowance
Price & chart
StateView.getSlot0
spot price and market cap · chart embedded from GeckoTerminal
Sells can never get less than the launch price: the pool holds no ETH liquidity below it. Selling brings the price back toward where it started.
Chapter 08

Fees

Fees accumulate per pool in LaunchHook: fees(poolId) returns the recipient and what is owed in ETH (owed0) and in the token (owed1). They are split when LaunchHook.collectFees(poolKey) is called, by anyone: from the token page’s Collect fees button or directly on the contract.

80 % Creator

the launch’s fees recipient

20 % Protocol

the hook’s TREASURY

Example: 1 % hook fee (FEE_BPS = 100), split 80 / 20 on collect

Buy, exact-in: 1 ETH in → the pool outputs X tokens
  fee              = 1 % of X        (in tokens, booked as owed1)
    creator  (80 %) = 0.8 % of X     → fees recipient
    protocol (20 %) = 0.2 % of X     → TREASURY       (token side)
  trader receives  = 99 % of X

Sell, exact-in: tokens in → the pool outputs 1 ETH
  fee              = 0.01 ETH          (native ETH, booked as owed0)
    creator  (80 %) = 0.008 ETH      → fees recipient
    protocol (20 %) = 0.002 ETH      → TREASURY       (ETH side)
  trader receives  = 0.99 ETH
This is the only fee: no launch fee, no LP fee, no transfer tax, no referral cut.

Payout rules

Who can collect
Anyone
the split is hard-coded: the caller can’t redirect it
Rounding
Recipient: floor(80 %)
the protocol gets the remainder
Tiny swaps
No fee below 100 wei
1 % of the amount rounds down to 0
Undeliverable ETH share
Parked in pendingNative
withdrawn by its owner with claimPending()
Token-side payouts
safeTransfer
sent directly, never parked
Change recipient
setFeesRecipient(poolId, to)
current recipient only · not in the app UI, call it on the explorer
Chapter 09

Creator earnings

Every launch has a fees recipient: your wallet by default, or any address chosen at launch. It earns 80 % of every swap fee on the pool, for as long as the pool exists.

Who earns
The fees recipient
the creator’s wallet by default, or any address chosen at launch
Share
80 % of every swap fee
for the life of the pool
Paid in
Token + ETH
the token from buys, ETH from sells
Pending
fees(poolId).owed0 / owed1
100 % booked, 80 % yours · the token page polls it every 10 s
How to claim
Collect fees
token page button → collectFees(poolKey)
Expiry
Never
fees stay booked until collected
Hand on the role
setFeesRecipient
current recipient only · nobody else, Greenwood included
There are no holder dividends: Greenwood does not pay token holders, and no part of the protocol’s 20 % is shared with them.
Chapter 10

No graduation

None

Every token is in its final Uniswap v4 pool from the moment it launches: the LP is burned to 0x…dEaD in the launch transaction. There is no bonding curve and nothing to graduate to.

Pool
Same pool, always
created at launch, stays forever
LP
Burned at launch
minted to 0x…dEaD in the launch transaction
Bonding curve
None
Threshold / milestone
None
Migration
None
What changes over time
Price and volume only
pool, fee and recipient rules stay identical
There is no migration, no new pool and no destination. The token trades on its final Uniswap v4 pool from block one.
Chapter 11

Contracts & addresses

Robinhood Chain

All Greenwood contracts are immutable: no proxies, no upgrade path. GreenwoodLaunchpad, LaunchHook and every GreenwoodToken have no owner: nobody can pause trading, change the fee or redirect a creator’s 80 %.

Robinhood ChainChain ID 4663 · ETHCurrent

GreenwoodLaunchpad

Deploys tokens, creates and seeds pools, burns the LP, launch registry

LaunchHook

Fixed 1 % swap fee, 80 / 20 split, on-chain TWAP oracle

Uniswap v4 PoolManager

Core AMM: every v4 pool lives here

Uniswap v4 PositionManager

LP position NFTs

UniversalRouter

Swaps from the app

V4Quoter

Swap quotes (simulated, hook included)

StateView

Pool price reads (getSlot0)

Permit2

Token allowances for sells

ETH (ERC-20 view)

6 decimals, same funds as ETH

Multicall3

Batched reads

The hook address carries its Uniswap v4 permissions in its lowest 14 bits: afterInitialize | beforeSwap | afterSwap | afterSwapReturnDelta (HOOK_FLAGS = 0x10C4). It is deployed with CREATE2 and a mined salt, and its constructor refuses any other address.

Who controls what

GreenwoodLaunchpad
No owner
permissionless, holds nothing between calls
LaunchHook
No owner
fee rate constant · POOL_MANAGER, TREASURY immutable
GreenwoodToken
No owner
renounced in the constructor
LP positions
0x…dEaD
PositionManager.ownerOf(nftId), shown live on every token page
Protocol share (20 %)
the treasury (deployer) is the TREASURY · no power over pools

Previous deployment (retired)

The first-generation launchpad is retired. Its pools (six tokens, $WOOD included) are hookless and pay a 1 % LP fee, and their LP positions were withdrawn from the locker by the deployer: they are neither locked nor burned. The guarantees on this page don’t apply to them. Do not build integrations against these contracts.
Chapter 12

Integration guide

For builders

Everything an explorer, a bot or an aggregator needs is on-chain: no API key, no indexer. The launchpad keeps a registry you can page through, and every pool is a standard Uniswap v4 pool.

1 · Reading token metadata

There is no contractURI() / EIP-7572: the metadata lives in the launch record, as a JSON string of at most 2,048 bytes stored on-chain, logo included.

import { createPublicClient, http } from "viem";

const client = createPublicClient({ transport: http("https://rpc.mainnet.chain.robinhood.com") });

// reverts BadArgs if the token was not launched by this launchpad
const launch = await client.readContract({
  address: LAUNCHPAD, abi: launchpadAbi, functionName: "launchOf", args: [token],
});

const meta = JSON.parse(launch.metadata);
// {
//   "description": "What this token is about",
//   "image":       "data:image/webp;base64,UklGR…",   // square logo ≤ 96 px, on-chain
//   "socials":     ["https://x.com/yourtoken"],
//   "website":     "https://yourtoken.xyz"
// }
name() / symbol()
Standard ERC-20
also in the Launch record
decimals()
18
every GreenwoodToken
Logo
metadata.image
data URI stored on-chain
Socials
metadata.socials / website
Creator
launch.creator
the launch transaction’s sender

2 · Discovering all launches

event Launched(
  address indexed creator,
  address indexed token,
  bytes32 poolId,
  uint256 nftId,       // burned LP position
  uint256 supply,
  int24   initTick,
  string  name,
  string  symbol
);                     // metadata is not in the event: read launchOf(token)

totalLaunches()                → uint256
getLaunches(start, stop)       → Launch[]    // [start, stop), clamped
launchesByCreator(creator)     → address[]
launchIndexByToken(token)      → index + 1   // 0 = not a Greenwood launch

Check the pool, not just the token

LaunchHook is shared: anyone can initialise a pool with it and becomes that pool’s fees recipient. That includes a second pool for a real Greenwood token, with another fee, tick spacing or quote currency, so launchIndexByToken(token) > 0 alone does not prove you have the right pool. Require poolId == launchOf(token).poolId, or equivalently the exact key { currency0: 0x0, currency1: token, fee: 0, tickSpacing: 200, hooks: LaunchHook }.

3 · Reading the v4 pool

// PoolKey of every Greenwood launch: ETH always sorts first
const key = {
  currency0:   "0x0000000000000000000000000000000000000000", // ETH
  currency1:   token,
  fee:         0,
  tickSpacing: 200,
  hooks:       GREENWOOD_HOOK,
};
poolId = keccak256(abi.encode(key))           // also stored in launch.poolId

StateView.getSlot0(poolId) → (sqrtPriceX96, tick, protocolFee, lpFee)
tokensPerUsdc = (sqrtPriceX96 / 2^96)^2       // both sides 18 decimals: no 10^12 factor
priceUsd      = 1 / tokensPerUsdc
marketCapUsd  = supply / tokensPerUsdc

PositionManager.ownerOf(launch.nftId) == 0x…dEaD   // the LP is burned

4 · Swapping

Call UniversalRouter.execute(commands, inputs, deadline) with command V4_SWAP (0x10) and the actions SWAP_EXACT_IN_SINGLE, SETTLE_ALL, TAKE_ALL. A buy, as the app sends it:

import { encodeAbiParameters, encodePacked } from "viem";

const actions = encodePacked(["uint8", "uint8", "uint8"], [0x06, 0x0c, 0x0f]);
const params = [
  encodeAbiParameters([exactInputSingleParams], [{
    poolKey: key, zeroForOne: true, amountIn, amountOutMinimum: minOut, hookData: "0x",
  }]),
  encodeAbiParameters([{ type: "address" }, { type: "uint256" }], [key.currency0, amountIn]), // SETTLE_ALL
  encodeAbiParameters([{ type: "address" }, { type: "uint256" }], [key.currency1, minOut]),   // TAKE_ALL
];
const input = encodeAbiParameters([{ type: "bytes" }, { type: "bytes[]" }], [actions, params]);

await walletClient.writeContract({
  address: UNIVERSAL_ROUTER, abi: universalRouterAbi, functionName: "execute",
  args: ["0x10", [input], deadline],
  value: amountIn, // native ETH, 18 decimals: no approval
});
Buy
zeroForOne = true
msg.value = ETH in, no approval
Sell
zeroForOne = false
token.approve(Permit2), then Permit2.approve(token, router, amount, expiry)
Quote
V4Quoter.quoteExactInputSingle
simulated · amountOut is net of the hook’s 1 %

Where to find what

Get logo
metadata.image
List tokens
getLaunches / Launched events
Current price
StateView.getSlot0(poolId)
TWAP
LaunchHook.consult(poolId, secondsAgo)
mean tick: price = 1.0001^tick tokens per ETH
Price history
LaunchHook.getCheckpoints(poolId, start, stop)
≤ 1 checkpoint per clock minute · the app’s own chart is a GeckoTerminal embed
Volume / trades
PoolManager Swap events
filter by poolId · there is no Greenwood REST API
Pending fees
LaunchHook.fees(poolId)
Chapter 13

On-chain reference

No API key

Greenwood has no public REST API: the app’s own server routes (RPC relay, analytics, source verification) are internal. Everything below is a contract call on Robinhood Chain, readable through any RPC.

Base

rpc        https://rpc.mainnet.chain.robinhood.com   // Robinhood Chain, chain ID 4663
launchpad  0x5710Fb3E6E9654D220ea3F3e495b7D376B2bD81B
hook       0x7D4963d6ADFD22350e1556c6b43897BcbFaad0c4

GreenwoodLaunchpad

WRITElaunch((name, symbol, metadata, supply, absTick, feesRecipient, salt)) payable → (token, poolId, nftId)
Anyone · msg.value = optional first buy
READtotalLaunches() → uint256
Number of launches
READlaunchAt(index) → Launch
Launch record by index
READlaunchOf(token) → Launch
Reverts BadArgs if not launched here
READgetLaunches(start, stop) → Launch[]
[start, stop), clamped
READlaunchesByCreator(creator) → address[]
Tokens launched by a wallet
READlaunchIndexByToken(token) → uint256
index + 1 · 0 = not a launch
CONSTPOOL_FEE = 0 · TICK_SPACING = 200 · DEAD · MAX_METADATA_BYTES = 2048 · HOOKS
Constants and immutables
EVENTLaunched(creator, token, poolId, nftId, supply, initTick, name, symbol)
creator and token indexed

LaunchHook

READfees(poolId) → (recipient, owed0, owed1)
Live recipient + uncollected fees
WRITEcollectFees(poolKey) → (amt0, amt1)
Anyone · 80 / 20 split · NoFees if empty
WRITEsetFeesRecipient(poolId, to)
Current fees recipient only
WRITEclaimPending()
Pull your parked ETH
READpendingNative(address) → uint256
Parked ETH
READcurrentTick(poolId) → int24
Spot tick
READconsult(poolId, secondsAgo) → int24
Mean tick, rounded toward −∞
READobserve(poolId, secondsAgos[]) → int64[]
Tick cumulatives
READlatest(poolId) → (timestamp, tickCumulative)
Last oracle write
READcheckpointCount(poolId) → uint256
History length
READgetCheckpoints(poolId, start, stop) → Observation[]
[start, stop), clamped
CONSTFEE_BPS = 100 · HOOK_FLAGS = 4292 · CHECKPOINT_INTERVAL = 60 · POOL_MANAGER · TREASURY
Constants and immutables, no setter
EVENTFeesCollected · FeesRecipientSet · OracleInitialized · PendingCredited · PendingClaimed
poolId / address indexed

Errors

ERRORBadArgs()
Bad launch params, unknown token in launchOf, zero address
ERRORBadTick()
absTick not a positive multiple of 200 below 887272
ERRORNotFeesRecipient()
setFeesRecipient from anyone but the current recipient
ERRORNoFees()
collectFees with nothing owed
ERRORNothingPending()
claimPending with nothing parked
ERRORTooOld() · NotInitialized()
Oracle query before the pool existed

Launch record

struct Launch {
  address token;
  bytes32 poolId;         // Uniswap v4 pool id
  uint256 nftId;          // LP position, owned by 0x…dEaD
  address creator;
  address feesRecipient;  // at launch; live: LaunchHook.fees(poolId).recipient
  uint64  timestamp;
  int24   initTick;       // start price
  uint256 supply;         // 18 decimals
  string  name;
  string  symbol;
  string  metadata;       // JSON: description, image, socials, website
}

Price history

getCheckpoints(poolId, start, stop) → Observation[]   // ≤ 1 per clock minute, the pool's whole life
struct Observation { uint48 timestamp; int64 tickCumulative; }

// exact TWAP between two consecutive checkpoints a and b:
meanTick      = (b.tickCumulative - a.tickCumulative) / (b.timestamp - a.timestamp)
tokensPerUsdc = 1.0001 ^ meanTick
priceUsd      = 1 / tokensPerUsdc
// live end of the series: observe(poolId, [0])
Token amounts and ETH both use 18 decimals; only the ERC-20 view of ETH (0x3600…0000) uses 6.